summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp29
-rw-r--r--scene/2d/animated_sprite.h3
-rw-r--r--scene/2d/area_2d.cpp5
-rw-r--r--scene/2d/audio_stream_player_2d.cpp41
-rw-r--r--scene/2d/audio_stream_player_2d.h7
-rw-r--r--scene/2d/canvas_item.cpp6
-rw-r--r--scene/2d/canvas_item.h2
-rw-r--r--scene/2d/canvas_modulate.cpp2
-rw-r--r--scene/2d/collision_object_2d.cpp11
-rw-r--r--scene/2d/cpu_particles_2d.cpp15
-rw-r--r--scene/2d/light_2d.cpp2
-rw-r--r--scene/2d/light_occluder_2d.cpp61
-rw-r--r--scene/2d/light_occluder_2d.h9
-rw-r--r--scene/2d/line_2d.cpp20
-rw-r--r--scene/2d/line_2d.h4
-rw-r--r--scene/2d/mesh_instance_2d.cpp2
-rw-r--r--scene/2d/navigation_2d.cpp12
-rw-r--r--scene/2d/navigation_polygon.cpp2
-rw-r--r--scene/2d/parallax_background.cpp2
-rw-r--r--scene/2d/parallax_layer.cpp5
-rw-r--r--scene/2d/path_2d.cpp26
-rw-r--r--scene/2d/physics_body_2d.cpp11
-rw-r--r--scene/2d/position_2d.cpp34
-rw-r--r--scene/2d/position_2d.h7
-rw-r--r--scene/2d/skeleton_2d.h6
-rw-r--r--scene/2d/tile_map.cpp144
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/area.cpp5
-rw-r--r--scene/3d/arvr_nodes.cpp60
-rw-r--r--scene/3d/arvr_nodes.h9
-rw-r--r--scene/3d/audio_stream_player_3d.cpp47
-rw-r--r--scene/3d/audio_stream_player_3d.h8
-rw-r--r--scene/3d/camera.cpp92
-rw-r--r--scene/3d/camera.h11
-rw-r--r--scene/3d/collision_object.cpp6
-rw-r--r--scene/3d/collision_shape.cpp34
-rw-r--r--scene/3d/collision_shape.h4
-rw-r--r--scene/3d/cpu_particles.cpp19
-rw-r--r--scene/3d/light.cpp4
-rw-r--r--scene/3d/mesh_instance.cpp2
-rw-r--r--scene/3d/navigation.cpp64
-rw-r--r--scene/3d/navigation_mesh.cpp110
-rw-r--r--scene/3d/navigation_mesh.h21
-rw-r--r--scene/3d/particles.cpp2
-rw-r--r--scene/3d/path.cpp8
-rw-r--r--scene/3d/physics_body.cpp25
-rw-r--r--scene/3d/physics_body.h2
-rw-r--r--scene/3d/proximity_group.cpp1
-rw-r--r--scene/3d/skeleton.cpp9
-rw-r--r--scene/3d/soft_body.cpp10
-rw-r--r--scene/3d/soft_body.h1
-rw-r--r--scene/3d/spatial.cpp24
-rw-r--r--scene/3d/vehicle_body.cpp16
-rw-r--r--scene/3d/vehicle_body.h3
-rw-r--r--scene/3d/visual_instance.cpp15
-rw-r--r--scene/3d/visual_instance.h4
-rw-r--r--scene/3d/voxel_light_baker.cpp6
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/animation/animation_cache.cpp3
-rw-r--r--scene/animation/animation_node_state_machine.cpp11
-rw-r--r--scene/animation/animation_player.cpp21
-rw-r--r--scene/animation/animation_tree.cpp50
-rw-r--r--scene/animation/animation_tree_player.cpp15
-rw-r--r--scene/animation/skeleton_ik.cpp34
-rw-r--r--scene/animation/skeleton_ik.h2
-rw-r--r--scene/animation/tween.cpp5
-rw-r--r--scene/audio/audio_stream_player.cpp136
-rw-r--r--scene/audio/audio_stream_player.h10
-rw-r--r--scene/gui/base_button.cpp237
-rw-r--r--scene/gui/base_button.h7
-rw-r--r--scene/gui/button.cpp2
-rw-r--r--scene/gui/check_button.cpp13
-rw-r--r--scene/gui/color_picker.cpp78
-rw-r--r--scene/gui/color_picker.h11
-rw-r--r--scene/gui/container.cpp2
-rw-r--r--scene/gui/control.cpp196
-rw-r--r--scene/gui/control.h12
-rw-r--r--scene/gui/file_dialog.cpp8
-rw-r--r--scene/gui/graph_edit.cpp6
-rw-r--r--scene/gui/graph_node.cpp6
-rw-r--r--scene/gui/item_list.cpp25
-rw-r--r--scene/gui/line_edit.cpp99
-rw-r--r--scene/gui/menu_button.cpp22
-rw-r--r--scene/gui/menu_button.h1
-rw-r--r--scene/gui/option_button.cpp30
-rw-r--r--scene/gui/option_button.h6
-rw-r--r--scene/gui/popup.cpp15
-rw-r--r--scene/gui/popup.h1
-rw-r--r--scene/gui/popup_menu.cpp201
-rw-r--r--scene/gui/popup_menu.h43
-rw-r--r--scene/gui/rich_text_label.cpp11
-rw-r--r--scene/gui/scroll_bar.cpp16
-rw-r--r--scene/gui/scroll_container.cpp13
-rw-r--r--scene/gui/slider.cpp16
-rw-r--r--scene/gui/spin_box.cpp1
-rw-r--r--scene/gui/tab_container.cpp57
-rw-r--r--scene/gui/tab_container.h3
-rw-r--r--scene/gui/tabs.cpp6
-rw-r--r--scene/gui/text_edit.cpp618
-rw-r--r--scene/gui/text_edit.h51
-rw-r--r--scene/gui/texture_button.cpp5
-rw-r--r--scene/gui/texture_rect.cpp79
-rw-r--r--scene/gui/texture_rect.h8
-rw-r--r--scene/gui/tree.cpp105
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/main/canvas_layer.cpp51
-rw-r--r--scene/main/canvas_layer.h10
-rw-r--r--scene/main/http_request.cpp2
-rw-r--r--scene/main/node.cpp54
-rw-r--r--scene/main/node.h18
-rw-r--r--scene/main/scene_tree.cpp16
-rw-r--r--scene/main/scene_tree.h4
-rw-r--r--scene/main/viewport.cpp137
-rw-r--r--scene/main/viewport.h7
-rw-r--r--scene/register_scene_types.cpp33
-rw-r--r--scene/resources/animation.cpp169
-rw-r--r--scene/resources/animation.h3
-rw-r--r--scene/resources/audio_stream_sample.cpp11
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/bit_map.cpp59
-rw-r--r--scene/resources/bit_map.h4
-rw-r--r--scene/resources/box_shape.cpp1
-rw-r--r--scene/resources/capsule_shape.cpp1
-rw-r--r--scene/resources/concave_polygon_shape.cpp1
-rw-r--r--scene/resources/convex_polygon_shape.cpp4
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp6
-rw-r--r--scene/resources/curve.cpp11
-rw-r--r--scene/resources/cylinder_shape.cpp1
-rw-r--r--scene/resources/default_theme/default_theme.cpp17
-rwxr-xr-xscene/resources/default_theme/make_header.py2
-rw-r--r--scene/resources/default_theme/space.pngbin0 -> 122 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h12
-rw-r--r--scene/resources/default_theme/toggle_off_disabled.pngbin0 -> 1676 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled.pngbin0 -> 1698 bytes
-rw-r--r--scene/resources/dynamic_font.cpp2
-rw-r--r--scene/resources/environment.cpp6
-rw-r--r--scene/resources/font.cpp41
-rw-r--r--scene/resources/font.h1
-rw-r--r--scene/resources/gradient.h2
-rw-r--r--scene/resources/height_map_shape.cpp205
-rw-r--r--scene/resources/height_map_shape.h62
-rw-r--r--scene/resources/material.cpp28
-rw-r--r--scene/resources/material.h1
-rw-r--r--scene/resources/mesh.cpp47
-rw-r--r--scene/resources/mesh.h12
-rw-r--r--scene/resources/mesh_library.cpp26
-rw-r--r--scene/resources/mesh_library.h3
-rw-r--r--scene/resources/multimesh.cpp76
-rw-r--r--scene/resources/multimesh.h9
-rw-r--r--scene/resources/packed_scene.cpp2
-rw-r--r--scene/resources/particles_material.cpp1
-rw-r--r--scene/resources/plane_shape.cpp1
-rw-r--r--scene/resources/primitive_meshes.cpp6
-rw-r--r--scene/resources/primitive_meshes.h1
-rw-r--r--scene/resources/ray_shape.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp74
-rw-r--r--scene/resources/resource_format_text.h8
-rw-r--r--scene/resources/shape.cpp5
-rw-r--r--scene/resources/shape.h2
-rw-r--r--scene/resources/sky.cpp10
-rw-r--r--scene/resources/sky.h2
-rw-r--r--scene/resources/sphere_shape.cpp1
-rw-r--r--scene/resources/style_box.cpp219
-rw-r--r--scene/resources/style_box.h12
-rw-r--r--scene/resources/surface_tool.cpp75
-rw-r--r--scene/resources/surface_tool.h9
-rw-r--r--scene/resources/texture.cpp150
-rw-r--r--scene/resources/texture.h43
-rw-r--r--scene/resources/tile_set.cpp87
-rw-r--r--scene/resources/tile_set.h21
-rw-r--r--scene/resources/visual_shader.cpp694
-rw-r--r--scene/resources/visual_shader.h95
-rw-r--r--scene/resources/visual_shader_nodes.cpp1248
-rw-r--r--scene/resources/visual_shader_nodes.h638
174 files changed, 6563 insertions, 1395 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index f8384bd1e4..25ad6bd5c9 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -393,19 +393,30 @@ void AnimatedSprite::_notification(int p_what) {
timeout = _get_frame_duration();
int fc = frames->get_frame_count(animation);
- if (frame >= fc - 1) {
+ if ((!backwards && frame >= fc - 1) || (backwards && frame <= 0)) {
if (frames->get_animation_loop(animation)) {
- frame = 0;
+ if (backwards)
+ frame = fc - 1;
+ else
+ frame = 0;
+
emit_signal(SceneStringNames::get_singleton()->animation_finished);
} else {
- frame = fc - 1;
+ if (backwards)
+ frame = 0;
+ else
+ frame = fc - 1;
+
if (!is_over) {
is_over = true;
emit_signal(SceneStringNames::get_singleton()->animation_finished);
}
}
} else {
- frame++;
+ if (backwards)
+ frame--;
+ else
+ frame++;
}
update();
@@ -594,10 +605,12 @@ bool AnimatedSprite::_is_playing() const {
return playing;
}
-void AnimatedSprite::play(const StringName &p_animation) {
+void AnimatedSprite::play(const StringName &p_animation, const bool p_backwards) {
if (p_animation)
set_animation(p_animation);
+
+ backwards = p_backwards;
_set_playing(true);
}
@@ -632,6 +645,9 @@ void AnimatedSprite::_reset_timeout() {
void AnimatedSprite::set_animation(const StringName &p_animation) {
+ ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1);
+
if (animation == p_animation)
return;
@@ -666,7 +682,7 @@ void AnimatedSprite::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_playing", "playing"), &AnimatedSprite::_set_playing);
ClassDB::bind_method(D_METHOD("_is_playing"), &AnimatedSprite::_is_playing);
- ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite::play, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite::play, DEFVAL(StringName()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite::stop);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite::is_playing);
@@ -713,6 +729,7 @@ AnimatedSprite::AnimatedSprite() {
frame = 0;
speed_scale = 1.0f;
playing = false;
+ backwards = false;
animation = "default";
timeout = 0;
is_over = false;
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index 8753f88799..2cc372bd93 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -128,6 +128,7 @@ class AnimatedSprite : public Node2D {
Ref<SpriteFrames> frames;
bool playing;
+ bool backwards;
StringName animation;
int frame;
float speed_scale;
@@ -169,7 +170,7 @@ public:
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
- void play(const StringName &p_animation = StringName());
+ void play(const StringName &p_animation = StringName(), const bool p_backwards = false);
void stop();
bool is_playing() const;
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 2a225e5797..b322cfe8f1 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -261,8 +261,9 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_
Map<ObjectID, AreaState>::Element *E = area_map.find(objid);
- ERR_FAIL_COND(!area_in && !E);
-
+ if (!area_in && !E) {
+ return; //likely removed from the tree
+ }
locked = true;
if (area_in) {
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 7b28ad2c12..73f583111b 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -37,7 +37,7 @@
void AudioStreamPlayer2D::_mix_audio() {
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade_out)) {
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -50,7 +50,7 @@ void AudioStreamPlayer2D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- if (stream_fade_out) {
+ if (stream_paused_fade_out) {
// Short fadeout ramp
buffer_size = MIN(buffer_size, 128);
}
@@ -84,10 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() {
}
//mix!
- AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
- AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
int cc = AudioServer::get_singleton()->get_channel_count();
@@ -139,15 +139,9 @@ void AudioStreamPlayer2D::_mix_audio() {
active = false;
}
- if (stream_stop) {
- active = false;
- set_physics_process_internal(false);
- setplay = -1;
- }
-
output_ready = false;
- stream_fade_in = false;
- stream_fade_out = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
void AudioStreamPlayer2D::_notification(int p_what) {
@@ -329,7 +323,6 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
}
if (stream_playback.is_valid()) {
- stream_stop = false;
active = true;
setplay = p_from_pos;
output_ready = false;
@@ -347,8 +340,9 @@ void AudioStreamPlayer2D::seek(float p_seconds) {
void AudioStreamPlayer2D::stop() {
if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade_out = true;
+ active = false;
+ set_physics_process_internal(false);
+ setplay = -1;
}
}
@@ -463,8 +457,8 @@ void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade_in = p_pause ? false : true;
- stream_fade_out = p_pause ? true : false;
+ stream_paused_fade_in = p_pause ? false : true;
+ stream_paused_fade_out = p_pause ? true : false;
}
}
@@ -473,6 +467,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const {
return stream_paused;
}
+Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
+ return stream_playback;
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
@@ -512,6 +510,8 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
+
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer2D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -543,9 +543,8 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() {
output_ready = false;
area_mask = 1;
stream_paused = false;
- stream_fade_in = false;
- stream_fade_out = false;
- stream_stop = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index caf5c6ee49..e9cdfa2303 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -73,9 +73,8 @@ private:
float pitch_scale;
bool autoplay;
bool stream_paused;
- bool stream_fade_in;
- bool stream_fade_out;
- bool stream_stop;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -130,6 +129,8 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ Ref<AudioStreamPlayback> get_stream_playback();
+
AudioStreamPlayer2D();
~AudioStreamPlayer2D();
};
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 2739f71543..78e98deb93 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -891,13 +891,13 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo
VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased);
}
-void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
+void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform, const Color &p_modulate) {
ERR_FAIL_COND(p_mesh.is_null());
RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid);
+ VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid);
}
void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
@@ -1168,7 +1168,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()));
+ ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map"), &CanvasItem::draw_multimesh, DEFVAL(Ref<Texture>()));
ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform);
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index bf7cfa8e75..a020ab5029 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -317,7 +317,7 @@ public:
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
- void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
+ void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1);
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index bd7bb97b03..009d664462 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -70,7 +70,7 @@ void CanvasModulate::_bind_methods() {
void CanvasModulate::set_color(const Color &p_color) {
color = p_color;
- if (is_inside_tree()) {
+ if (is_visible_in_tree()) {
VS::get_singleton()->canvas_set_modulate(get_canvas(), color);
}
}
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index d54070df8d..375375285d 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "collision_object_2d.h"
+
#include "scene/scene_string_names.h"
#include "servers/physics_2d_server.h"
@@ -56,7 +57,7 @@ void CollisionObject2D::_notification(int p_what) {
_update_pickable();
//get space
- }
+ } break;
case NOTIFICATION_ENTER_CANVAS: {
@@ -64,7 +65,7 @@ void CollisionObject2D::_notification(int p_what) {
Physics2DServer::get_singleton()->area_attach_canvas_instance_id(rid, get_canvas_layer_instance_id());
else
Physics2DServer::get_singleton()->body_attach_canvas_instance_id(rid, get_canvas_layer_instance_id());
- }
+ } break;
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -101,7 +102,7 @@ void CollisionObject2D::_notification(int p_what) {
Physics2DServer::get_singleton()->area_attach_canvas_instance_id(rid, 0);
else
Physics2DServer::get_singleton()->body_attach_canvas_instance_id(rid, 0);
- }
+ } break;
}
}
@@ -250,9 +251,9 @@ void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2
s.index = total_subshapes;
s.shape = p_shape;
if (area) {
- Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform);
+ Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
} else {
- Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform);
+ Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
}
sd.shapes.push_back(s);
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 4fafbd05f0..ff27e0a29a 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -37,7 +37,8 @@
void CPUParticles2D::set_emitting(bool p_emitting) {
emitting = p_emitting;
- set_process_internal(true);
+ if (emitting)
+ set_process_internal(true);
}
void CPUParticles2D::set_amount(int p_amount) {
@@ -574,7 +575,7 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (restart_time >= prev_time && restart_time < time) {
restart = true;
if (fractional_delta) {
- local_delta = (time - restart_time) * lifetime;
+ local_delta = time - restart_time;
}
}
@@ -582,13 +583,13 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (restart_time >= prev_time) {
restart = true;
if (fractional_delta) {
- local_delta = (lifetime - restart_time + time) * lifetime;
+ local_delta = lifetime - restart_time + time;
}
} else if (restart_time < time) {
restart = true;
if (fractional_delta) {
- local_delta = (time - restart_time) * lifetime;
+ local_delta = time - restart_time;
}
}
}
@@ -965,7 +966,7 @@ void CPUParticles2D::_update_render_thread() {
void CPUParticles2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- _set_redraw(true);
+ set_process_internal(emitting);
}
if (p_what == NOTIFICATION_EXIT_TREE) {
@@ -977,7 +978,7 @@ void CPUParticles2D::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
if (!redraw)
- return; // dont add to render list
+ return; // don't add to render list
RID texrid;
if (texture.is_valid()) {
@@ -1001,7 +1002,6 @@ void CPUParticles2D::_notification(int p_what) {
float delta = get_process_delta_time();
if (emitting) {
- _set_redraw(true);
inactive_time = 0;
} else {
inactive_time += delta;
@@ -1017,6 +1017,7 @@ void CPUParticles2D::_notification(int p_what) {
return;
}
}
+ _set_redraw(true);
if (time == 0 && pre_process_time > 0.0) {
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index d1fce74085..6b12db9e44 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -438,7 +438,7 @@ void Light2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_energy", "get_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix,Mask"), "set_mode", "get_mode");
ADD_GROUP("Range", "range_");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "range_height", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "range_height", PROPERTY_HINT_RANGE, "-2048,2048,0.1,or_lesser,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_layer_min", PROPERTY_HINT_RANGE, "-512,512,1"), "set_layer_range_min", "get_layer_range_min");
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 5334caed67..3a3f90ac4b 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -32,9 +32,59 @@
#include "core/engine.h"
+#define LINE_GRAB_WIDTH 8
+Rect2 OccluderPolygon2D::_edit_get_rect() const {
+
+ if (rect_cache_dirty) {
+ if (closed) {
+ PoolVector<Vector2>::Read r = polygon.read();
+ item_rect = Rect2();
+ for (int i = 0; i < polygon.size(); i++) {
+ Vector2 pos = r[i];
+ if (i == 0)
+ item_rect.position = pos;
+ else
+ item_rect.expand_to(pos);
+ }
+ rect_cache_dirty = false;
+ } else {
+ if (polygon.size() == 0) {
+ item_rect = Rect2();
+ } else {
+ Vector2 d = Vector2(LINE_GRAB_WIDTH, LINE_GRAB_WIDTH);
+ item_rect = Rect2(polygon[0] - d, 2 * d);
+ for (int i = 1; i < polygon.size(); i++) {
+ item_rect.expand_to(polygon[i] - d);
+ item_rect.expand_to(polygon[i] + d);
+ }
+ }
+ }
+ }
+
+ return item_rect;
+}
+
+bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+
+ if (closed) {
+ return Geometry::is_point_in_polygon(p_point, Variant(polygon));
+ } else {
+ const real_t d = LINE_GRAB_WIDTH / 2 + p_tolerance;
+ PoolVector<Vector2>::Read points = polygon.read();
+ for (int i = 0; i < polygon.size() - 1; i++) {
+ Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]);
+ if (p.distance_to(p_point) <= d)
+ return true;
+ }
+
+ return false;
+ }
+}
+
void OccluderPolygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) {
polygon = p_polygon;
+ rect_cache_dirty = true;
VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed);
emit_changed();
}
@@ -100,6 +150,7 @@ OccluderPolygon2D::OccluderPolygon2D() {
occ_polygon = VS::get_singleton()->canvas_occluder_polygon_create();
closed = true;
cull = CULL_DISABLED;
+ rect_cache_dirty = true;
}
OccluderPolygon2D::~OccluderPolygon2D() {
@@ -164,6 +215,16 @@ void LightOccluder2D::_notification(int p_what) {
}
}
+Rect2 LightOccluder2D::_edit_get_rect() const {
+
+ return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2();
+}
+
+bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+
+ return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false;
+}
+
void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) {
#ifdef DEBUG_ENABLED
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 498d65d764..7931cd0b30 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -50,10 +50,16 @@ private:
bool closed;
CullMode cull;
+ mutable Rect2 item_rect;
+ mutable bool rect_cache_dirty;
+
protected:
static void _bind_methods();
public:
+ virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
void set_polygon(const PoolVector<Vector2> &p_polygon);
PoolVector<Vector2> get_polygon() const;
@@ -85,6 +91,9 @@ protected:
static void _bind_methods();
public:
+ virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon);
Ref<OccluderPolygon2D> get_occluder_polygon() const;
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 105eb82afb..ba06b3ebff 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -112,8 +112,20 @@ int Line2D::get_point_count() const {
return _points.size();
}
-void Line2D::add_point(Vector2 pos) {
- _points.append(pos);
+void Line2D::clear_points() {
+ int count = _points.size();
+ if (count > 0) {
+ _points.resize(0);
+ update();
+ }
+}
+
+void Line2D::add_point(Vector2 pos, int atpos) {
+ if (atpos < 0 || _points.size() < atpos) {
+ _points.append(pos);
+ } else {
+ _points.insert(atpos, pos);
+ }
update();
}
@@ -310,9 +322,11 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Line2D::get_point_count);
- ClassDB::bind_method(D_METHOD("add_point", "position"), &Line2D::add_point);
+ ClassDB::bind_method(D_METHOD("add_point", "position", "at_position"), &Line2D::add_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("remove_point", "i"), &Line2D::remove_point);
+ ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points);
+
ClassDB::bind_method(D_METHOD("set_width", "width"), &Line2D::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &Line2D::get_width);
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 5bbd38e460..8a6f7b2dc5 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -70,7 +70,9 @@ public:
int get_point_count() const;
- void add_point(Vector2 pos);
+ void clear_points();
+
+ void add_point(Vector2 pos, int atpos = -1);
void remove_point(int i);
void set_width(float width);
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index b382ca7b33..bcd4bca940 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -50,6 +50,8 @@ void MeshInstance2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MeshInstance2D::set_normal_map);
ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map);
+ ADD_SIGNAL(MethodInfo("texture_changed"));
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp
index 57e0a5b118..72b5f2fb12 100644
--- a/scene/2d/navigation_2d.cpp
+++ b/scene/2d/navigation_2d.cpp
@@ -542,7 +542,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
//process
- if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
+ if (Math::is_zero_approx(portal_left.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
left_poly = p;
portal_left = left;
} else {
@@ -552,7 +552,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
left_poly = p;
portal_left = apex_point;
portal_right = apex_point;
- if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+ if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
path.push_back(apex_point);
skip = true;
}
@@ -560,7 +560,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) {
//process
- if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
+ if (Math::is_zero_approx(portal_right.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
right_poly = p;
portal_right = right;
} else {
@@ -570,7 +570,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
right_poly = p;
portal_right = apex_point;
portal_left = apex_point;
- if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+ if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
path.push_back(apex_point);
}
}
@@ -596,7 +596,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
}
}
- if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) {
+ if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(begin_point))) {
path.push_back(begin_point); // Add the begin point
} else {
path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point
@@ -604,7 +604,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
path.invert();
- if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) {
+ if (path.size() <= 1 || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(end_point))) {
path.push_back(end_point); // Add the end point
} else {
path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
index 0f6af358bd..e389d5f98f 100644
--- a/scene/2d/navigation_polygon.cpp
+++ b/scene/2d/navigation_polygon.cpp
@@ -312,7 +312,7 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines);
ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines);
- ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
}
diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp
index 96e13396c5..4ead1bbd1e 100644
--- a/scene/2d/parallax_background.cpp
+++ b/scene/2d/parallax_background.cpp
@@ -207,7 +207,7 @@ void ParallaxBackground::_bind_methods() {
ParallaxBackground::ParallaxBackground() {
scale = 1.0;
- set_layer(-1); //behind all by default
+ set_layer(-100); //behind all by default
base_scale = Vector2(1, 1);
ignore_camera_zoom = false;
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index baf5b5967b..9a6b63b9a3 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -105,6 +105,11 @@ void ParallaxLayer::_notification(int p_what) {
orig_scale = get_scale();
_update_mirroring();
} break;
+ case NOTIFICATION_EXIT_TREE: {
+
+ set_position(orig_offset);
+ set_scale(orig_scale);
+ } break;
}
}
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index fec861ad2f..e062067248 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -95,7 +95,7 @@ void Path2D::_notification(int p_what) {
return;
}
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
const float line_width = 2 * EDSCALE;
#else
const float line_width = 2;
@@ -264,7 +264,7 @@ void PathFollow2D::_validate_property(PropertyInfo &property) const {
if (path && path->get_curve().is_valid())
max = path->get_curve()->get_baked_length();
- property.hint_string = "0," + rtos(max) + ",0.01,or_greater";
+ property.hint_string = "0," + rtos(max) + ",0.01,or_lesser";
}
}
@@ -306,8 +306,8 @@ void PathFollow2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead);
ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotate"), "set_rotate", "is_rotating");
@@ -319,8 +319,24 @@ void PathFollow2D::_bind_methods() {
void PathFollow2D::set_offset(float p_offset) {
offset = p_offset;
- if (path)
+ if (path) {
+ if (path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {
+ float path_length = path->get_curve()->get_baked_length();
+
+ if (loop) {
+ while (offset > path_length)
+ offset -= path_length;
+
+ while (offset < 0)
+ offset += path_length;
+
+ } else {
+ offset = CLAMP(offset, 0, path_length);
+ }
+ }
+
_update_transform();
+ }
_change_notify("offset");
_change_notify("unit_offset");
}
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 1b7e1a6b8b..578c9aa5f9 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -199,7 +199,7 @@ real_t StaticBody2D::get_constant_angular_velocity() const {
#ifndef DISABLE_DEPRECATED
void StaticBody2D::set_friction(real_t p_friction) {
- if (p_friction == 1.0) { // default value, don't create an override for that
+ if (p_friction == 1.0 && physics_material_override.is_null()) { // default value, don't create an override for that
return;
}
@@ -229,7 +229,7 @@ real_t StaticBody2D::get_friction() const {
void StaticBody2D::set_bounce(real_t p_bounce) {
- if (p_bounce == 0.0) { // default value, don't create an override for that
+ if (p_bounce == 0.0 && physics_material_override.is_null()) { // default value, don't create an override for that
return;
}
@@ -626,7 +626,7 @@ real_t RigidBody2D::get_weight() const {
#ifndef DISABLE_DEPRECATED
void RigidBody2D::set_friction(real_t p_friction) {
- if (p_friction == 1.0) { // default value, don't create an override for that
+ if (p_friction == 1.0 && physics_material_override.is_null()) { // default value, don't create an override for that
return;
}
@@ -655,7 +655,7 @@ real_t RigidBody2D::get_friction() const {
void RigidBody2D::set_bounce(real_t p_bounce) {
- if (p_bounce == 0.0) { // default value, don't create an override for that
+ if (p_bounce == 0.0 && physics_material_override.is_null()) { // default value, don't create an override for that
return;
}
@@ -1275,9 +1275,6 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
if (collided) {
found_collision = true;
- }
-
- if (collided) {
colliders.push_back(collision);
motion = collision.remainder;
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index bed6f8a816..8bccf117fd 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -35,13 +35,15 @@
void Position2D::_draw_cross() {
- draw_line(Point2(-10, 0), Point2(+10, 0), Color(1, 0.5, 0.5));
- draw_line(Point2(0, -10), Point2(0, +10), Color(0.5, 1, 0.5));
+ float extents = get_gizmo_extents();
+ draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(1, 0.5, 0.5));
+ draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.5, 1, 0.5));
}
Rect2 Position2D::_edit_get_rect() const {
- return Rect2(Point2(-10, -10), Size2(20, 20));
+ float extents = get_gizmo_extents();
+ return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2));
}
bool Position2D::_edit_use_rect() const {
@@ -66,5 +68,31 @@ void Position2D::_notification(int p_what) {
}
}
+void Position2D::set_gizmo_extents(float p_extents) {
+ if (p_extents == DEFAULT_GIZMO_EXTENTS) {
+ set_meta("_gizmo_extents_", Variant());
+ } else {
+ set_meta("_gizmo_extents_", p_extents);
+ }
+
+ update();
+}
+
+float Position2D::get_gizmo_extents() const {
+ if (has_meta("_gizmo_extents_")) {
+ return get_meta("_gizmo_extents_");
+ } else {
+ return DEFAULT_GIZMO_EXTENTS;
+ }
+}
+
+void Position2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents);
+ ClassDB::bind_method(D_METHOD("_get_gizmo_extents"), &Position2D::get_gizmo_extents);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_gizmo_extents", "_get_gizmo_extents");
+}
+
Position2D::Position2D() {
}
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
index c95315fea3..dc9cc2df15 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -37,14 +37,21 @@ class Position2D : public Node2D {
GDCLASS(Position2D, Node2D)
+ const float DEFAULT_GIZMO_EXTENTS = 10.0;
+
void _draw_cross();
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+
+ void set_gizmo_extents(float p_extents);
+ float get_gizmo_extents() const;
+
Position2D();
};
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index cf9877e6f8..d24c0a1561 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -39,6 +39,9 @@ class Bone2D : public Node2D {
GDCLASS(Bone2D, Node2D)
friend class Skeleton2D;
+#ifdef TOOLS_ENABLED
+ friend class AnimatedValuesBackup;
+#endif
Bone2D *parent_bone;
Skeleton2D *skeleton;
@@ -71,6 +74,9 @@ class Skeleton2D : public Node2D {
GDCLASS(Skeleton2D, Node2D);
friend class Bone2D;
+#ifdef TOOLS_ENABLED
+ friend class AnimatedValuesBackup;
+#endif
struct Bone {
bool operator<(const Bone &p_bone) const {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index ed0a9c4915..2b43861eea 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -202,47 +202,27 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
Size2 s = p_sc;
Vector2 offset = p_offset;
- if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT)
- offset.y += cell_size.y;
- else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset += cell_size / 2;
- }
-
- if (s.y > s.x) {
- if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose))
- offset.y += s.y - s.x;
- } else if (s.y < s.x) {
- if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose))
- offset.x += s.x - s.y;
- }
-
if (p_cell.transpose) {
SWAP(xform.elements[0].x, xform.elements[0].y);
SWAP(xform.elements[1].x, xform.elements[1].y);
SWAP(offset.x, offset.y);
SWAP(s.x, s.y);
}
+
if (p_cell.flip_h) {
xform.elements[0].x = -xform.elements[0].x;
xform.elements[1].x = -xform.elements[1].x;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT)
- offset.x = s.x - offset.x;
- else if (tile_origin == TILE_ORIGIN_CENTER)
- offset.x = s.x - offset.x / 2;
+ offset.x = s.x - offset.x;
}
+
if (p_cell.flip_v) {
xform.elements[0].y = -xform.elements[0].y;
xform.elements[1].y = -xform.elements[1].y;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT)
- offset.y = s.y - offset.y;
- else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- offset.y += s.y;
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset.y += s.y;
- }
+ offset.y = s.y - offset.y;
}
- xform.elements[2].x += offset.x;
- xform.elements[2].y += offset.y;
+
+ offset += cell_size / 2 - s / 2;
+ xform.elements[2] += offset;
}
void TileMap::update_dirty_quadrants() {
@@ -390,64 +370,25 @@ void TileMap::update_dirty_quadrants() {
rect.size.x += fp_adjust;
rect.size.y += fp_adjust;
- if (rect.size.y > rect.size.x) {
- if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose))
- tile_ofs.y += rect.size.y - rect.size.x;
- } else if (rect.size.y < rect.size.x) {
- if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose))
- tile_ofs.x += rect.size.x - rect.size.y;
- }
-
- /* rect.size.x+=fp_adjust;
- rect.size.y+=fp_adjust;*/
-
- if (c.transpose)
+ if (c.transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
+ rect.position.x += cell_size.x / 2 - rect.size.y / 2;
+ rect.position.y += cell_size.y / 2 - rect.size.x / 2;
+ } else {
+ rect.position += cell_size / 2 - rect.size / 2;
+ }
if (c.flip_h) {
rect.size.x = -rect.size.x;
tile_ofs.x = -tile_ofs.x;
}
+
if (c.flip_v) {
rect.size.y = -rect.size.y;
tile_ofs.y = -tile_ofs.y;
}
- Vector2 center_ofs;
-
- if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
- rect.position += tile_ofs;
-
- } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
-
- rect.position += tile_ofs;
-
- if (c.transpose) {
- if (c.flip_h)
- rect.position.x -= cell_size.x;
- else
- rect.position.x += cell_size.x;
- } else {
- if (c.flip_v)
- rect.position.y -= cell_size.y;
- else
- rect.position.y += cell_size.y;
- }
-
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
-
- rect.position += tile_ofs;
-
- if (c.flip_h)
- rect.position.x -= cell_size.x / 2;
- else
- rect.position.x += cell_size.x / 2;
-
- if (c.flip_v)
- rect.position.y -= cell_size.y / 2;
- else
- rect.position.y += cell_size.y / 2;
- }
+ rect.position += tile_ofs;
Ref<Texture> normal_map = tile_set->tile_get_normal_map(c.id);
Color modulate = tile_set->tile_get_modulate(c.id);
@@ -471,7 +412,7 @@ void TileMap::update_dirty_quadrants() {
Vector2 shape_ofs = shapes[j].shape_transform.get_origin();
- _fix_cell_transform(xform, c, shape_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, shape_ofs, s);
xform *= shapes[j].shape_transform.untranslated();
@@ -491,7 +432,7 @@ void TileMap::update_dirty_quadrants() {
shape_idx++;
#ifdef DEBUG_ENABLED
} else {
- print_error("The TileSet asigned to the TileMap " + get_name() + " has an invalid convex shape.");
+ print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape.");
#endif
}
}
@@ -523,7 +464,7 @@ void TileMap::update_dirty_quadrants() {
if (navpoly.is_valid()) {
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, npoly_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, npoly_ofs, s);
int pid = navigation->navpoly_add(navpoly, nav_rel * xform);
@@ -573,7 +514,7 @@ void TileMap::update_dirty_quadrants() {
}
Transform2D navxform;
navxform.set_origin(offset.floor());
- _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s);
+ _fix_cell_transform(navxform, c, npoly_ofs, s);
vs->canvas_item_set_transform(debug_navigation_item, navxform);
vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors);
@@ -593,7 +534,7 @@ void TileMap::update_dirty_quadrants() {
Vector2 occluder_ofs = tile_set->tile_get_occluder_offset(c.id);
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, occluder_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, occluder_ofs, s);
RID orid = VS::get_singleton()->canvas_light_occluder_create();
VS::get_singleton()->canvas_light_occluder_set_transform(orid, get_global_transform() * xform);
@@ -749,7 +690,10 @@ void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_
void TileMap::_set_celld(const Vector2 &p_pos, const Dictionary &p_data) {
- set_cell(p_pos.x, p_pos.y, p_data["id"], p_data["flip_h"], p_data["flip_y"], p_data["transpose"], p_data["auto_coord"]);
+ 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 };
+ Variant::CallError ce;
+ call("set_cell", args, 7, ce);
}
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) {
@@ -773,6 +717,7 @@ void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_
else
_make_quadrant_dirty(Q);
+ used_size_cache_dirty = true;
return;
}
@@ -923,8 +868,17 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
_make_quadrant_dirty(Q);
} else if (tile_set->tile_get_tile_mode(id) == TileSet::SINGLE_TILE) {
+
E->get().autotile_coord_x = 0;
E->get().autotile_coord_y = 0;
+ } else if (tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
+
+ if (tile_set->autotile_get_bitmask(id, Vector2(p_x, p_y)) == TileSet::BIND_CENTER) {
+ Vector2 coord = tile_set->atlastile_get_subtile_by_priority(id, this, Vector2(p_x, p_y));
+
+ E->get().autotile_coord_x = (int)coord.x;
+ E->get().autotile_coord_y = (int)coord.y;
+ }
}
}
}
@@ -1391,18 +1345,21 @@ Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const {
if (!p_ignore_ofs) {
switch (half_offset) {
- case HALF_OFFSET_X: {
+ case HALF_OFFSET_X:
+ case HALF_OFFSET_NEGATIVE_X: {
if (ABS(p_y) & 1) {
- ret += get_cell_transform()[0] * 0.5;
+ ret += get_cell_transform()[0] * (half_offset == HALF_OFFSET_X ? 0.5 : -0.5);
}
} break;
- case HALF_OFFSET_Y: {
+ case HALF_OFFSET_Y:
+ case HALF_OFFSET_NEGATIVE_Y: {
if (ABS(p_x) & 1) {
- ret += get_cell_transform()[1] * 0.5;
+ ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5);
}
} break;
- default: {}
+ default: {
+ }
}
}
return ret;
@@ -1462,12 +1419,23 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const {
ret.x -= 0.5;
}
} break;
+ case HALF_OFFSET_NEGATIVE_X: {
+ if (ret.y > 0 ? int(ret.y) & 1 : (int(ret.y) - 1) & 1) {
+ ret.x += 0.5;
+ }
+ } break;
case HALF_OFFSET_Y: {
if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) {
ret.y -= 0.5;
}
} break;
- default: {}
+ case HALF_OFFSET_NEGATIVE_Y: {
+ if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) {
+ ret.y += 0.5;
+ }
+ } break;
+ default: {
+ }
}
// Account for precision errors on the border (GH-23250).
@@ -1677,7 +1645,7 @@ void TileMap::_bind_methods() {
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"), "set_half_offset", "get_half_offset");
+ 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_mode", "is_y_sort_mode_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv");
@@ -1703,6 +1671,8 @@ void TileMap::_bind_methods() {
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);
BIND_ENUM_CONSTANT(TILE_ORIGIN_TOP_LEFT);
BIND_ENUM_CONSTANT(TILE_ORIGIN_CENTER);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index e450e1e256..6a1467aa48 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -52,6 +52,8 @@ public:
HALF_OFFSET_X,
HALF_OFFSET_Y,
HALF_OFFSET_DISABLED,
+ HALF_OFFSET_NEGATIVE_X,
+ HALF_OFFSET_NEGATIVE_Y,
};
enum TileOrigin {
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index e58e26d2d1..3557f0425c 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -356,7 +356,9 @@ void Area::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ar
Map<ObjectID, AreaState>::Element *E = area_map.find(objid);
- ERR_FAIL_COND(!area_in && !E);
+ if (!area_in && !E) {
+ return; //likely removed from the tree
+ }
locked = true;
@@ -756,7 +758,6 @@ Area::Area() :
monitorable = false;
collision_mask = 1;
collision_layer = 1;
- set_ray_pickable(false);
set_monitoring(true);
set_monitorable(true);
diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp
index 17b698c1b8..4e88948ce2 100644
--- a/scene/3d/arvr_nodes.cpp
+++ b/scene/3d/arvr_nodes.cpp
@@ -127,7 +127,7 @@ Point2 ARVRCamera::unproject_position(const Vector3 &p_pos) const {
return res;
};
-Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
+Vector3 ARVRCamera::project_position(const Point2 &p_point, float p_z_depth) const {
// get our ARVRServer
ARVRServer *arvr_server = ARVRServer::get_singleton();
ERR_FAIL_NULL_V(arvr_server, Vector3());
@@ -135,7 +135,7 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface();
if (arvr_interface.is_null()) {
// we might be in the editor or have VR turned off, just call superclass
- return Camera::project_position(p_point);
+ return Camera::project_position(p_point, p_z_depth);
}
if (!is_inside_tree()) {
@@ -155,7 +155,7 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0;
point *= vp_size;
- Vector3 p(point.x, point.y, -get_znear());
+ Vector3 p(point.x, point.y, -p_z_depth);
return get_camera_transform().xform(p);
};
@@ -233,6 +233,13 @@ void ARVRController::_notification(int p_what) {
} else {
button_states = 0;
};
+
+ // check for an updated mesh
+ Ref<Mesh> trackerMesh = tracker->get_mesh();
+ if (mesh != trackerMesh) {
+ mesh = trackerMesh;
+ emit_signal("mesh_updated", mesh);
+ }
};
}; break;
default:
@@ -258,8 +265,11 @@ void ARVRController::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &ARVRController::set_rumble);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble");
+ ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRController::get_mesh);
+
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button")));
ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button")));
+ ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")));
};
void ARVRController::set_controller_id(int p_controller_id) {
@@ -341,6 +351,10 @@ void ARVRController::set_rumble(real_t p_rumble) {
};
};
+Ref<Mesh> ARVRController::get_mesh() const {
+ return mesh;
+}
+
bool ARVRController::get_is_active() const {
return is_active;
};
@@ -365,11 +379,11 @@ String ARVRController::get_configuration_warning() const {
// must be child node of ARVROrigin!
ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent());
if (origin == NULL) {
- return TTR("ARVRController must have an ARVROrigin node as its parent");
+ return TTR("ARVRController must have an ARVROrigin node as its parent.");
};
if (controller_id == 0) {
- return TTR("The controller id must not be 0 or this controller will not be bound to an actual controller");
+ return TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller.");
};
return String();
@@ -423,6 +437,13 @@ void ARVRAnchor::_notification(int p_what) {
// apply our reference frame and set our transform
set_transform(arvr_server->get_reference_frame() * transform);
+
+ // check for an updated mesh
+ Ref<Mesh> trackerMesh = tracker->get_mesh();
+ if (mesh != trackerMesh) {
+ mesh = trackerMesh;
+ emit_signal("mesh_updated", mesh);
+ }
};
}; break;
default:
@@ -441,6 +462,9 @@ void ARVRAnchor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_size"), &ARVRAnchor::get_size);
ClassDB::bind_method(D_METHOD("get_plane"), &ARVRAnchor::get_plane);
+
+ ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRAnchor::get_mesh);
+ ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")));
};
void ARVRAnchor::set_anchor_id(int p_anchor_id) {
@@ -482,11 +506,11 @@ String ARVRAnchor::get_configuration_warning() const {
// must be child node of ARVROrigin!
ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent());
if (origin == NULL) {
- return TTR("ARVRAnchor must have an ARVROrigin node as its parent");
+ return TTR("ARVRAnchor must have an ARVROrigin node as its parent.");
};
if (anchor_id == 0) {
- return TTR("The anchor id must not be 0 or this anchor will not be bound to an actual anchor");
+ return TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor.");
};
return String();
@@ -501,6 +525,10 @@ Plane ARVRAnchor::get_plane() const {
return plane;
};
+Ref<Mesh> ARVRAnchor::get_mesh() const {
+ return mesh;
+}
+
ARVRAnchor::ARVRAnchor() {
anchor_id = 0;
is_active = true;
@@ -517,7 +545,7 @@ String ARVROrigin::get_configuration_warning() const {
return String();
if (tracked_camera == NULL)
- return TTR("ARVROrigin requires an ARVRCamera child node");
+ return TTR("ARVROrigin requires an ARVRCamera child node.");
return String();
};
@@ -555,6 +583,10 @@ void ARVROrigin::set_world_scale(float p_world_scale) {
};
void ARVROrigin::_notification(int p_what) {
+ // get our ARVRServer
+ ARVRServer *arvr_server = ARVRServer::get_singleton();
+ ERR_FAIL_NULL(arvr_server);
+
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
set_process_internal(true);
@@ -563,10 +595,6 @@ void ARVROrigin::_notification(int p_what) {
set_process_internal(false);
}; break;
case NOTIFICATION_INTERNAL_PROCESS: {
- // get our ARVRServer
- ARVRServer *arvr_server = ARVRServer::get_singleton();
- ERR_FAIL_NULL(arvr_server);
-
// set our world origin to our node transform
arvr_server->set_world_origin(get_global_transform());
@@ -583,6 +611,14 @@ void ARVROrigin::_notification(int p_what) {
default:
break;
};
+
+ // send our notification to all active ARVR interfaces, they may need to react to it also
+ for (int i = 0; i < arvr_server->get_interface_count(); i++) {
+ Ref<ARVRInterface> interface = arvr_server->get_interface(i);
+ if (interface.is_valid() && interface->is_initialized()) {
+ interface->notification(p_what);
+ }
+ }
};
ARVROrigin::ARVROrigin() {
diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h
index 523bc112c1..8e735f7110 100644
--- a/scene/3d/arvr_nodes.h
+++ b/scene/3d/arvr_nodes.h
@@ -33,6 +33,7 @@
#include "scene/3d/camera.h"
#include "scene/3d/spatial.h"
+#include "scene/resources/mesh.h"
#include "servers/arvr/arvr_positional_tracker.h"
/**
@@ -54,7 +55,7 @@ public:
virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const;
virtual Point2 unproject_position(const Vector3 &p_pos) const;
- virtual Vector3 project_position(const Point2 &p_point) const;
+ virtual Vector3 project_position(const Point2 &p_point, float p_z_depth = 0) const;
virtual Vector<Plane> get_frustum() const;
ARVRCamera();
@@ -75,6 +76,7 @@ private:
int controller_id;
bool is_active;
int button_states;
+ Ref<Mesh> mesh;
protected:
void _notification(int p_what);
@@ -95,6 +97,8 @@ public:
bool get_is_active() const;
ARVRPositionalTracker::TrackerHand get_hand() const;
+ Ref<Mesh> get_mesh(void) const;
+
String get_configuration_warning() const;
ARVRController();
@@ -113,6 +117,7 @@ private:
int anchor_id;
bool is_active;
Vector3 size;
+ Ref<Mesh> mesh;
protected:
void _notification(int p_what);
@@ -128,6 +133,8 @@ public:
Plane get_plane() const;
+ Ref<Mesh> get_mesh(void) const;
+
String get_configuration_warning() const;
ARVRAnchor();
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 4b3934c4ea..ff8c218575 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -38,7 +38,7 @@
void AudioStreamPlayer3D::_mix_audio() {
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade_out)) {
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -53,7 +53,7 @@ void AudioStreamPlayer3D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- if (stream_fade_out) {
+ if (stream_paused_fade_out) {
// Short fadeout ramp
buffer_size = MIN(buffer_size, 128);
}
@@ -109,10 +109,10 @@ void AudioStreamPlayer3D::_mix_audio() {
int buffers = AudioServer::get_singleton()->get_channel_count();
for (int k = 0; k < buffers; k++) {
- AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k];
- AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k];
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k];
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k];
AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k))
continue; //may have been deleted, will be updated on process
@@ -198,15 +198,9 @@ void AudioStreamPlayer3D::_mix_audio() {
active = false;
}
- if (stream_stop) {
- active = false;
- set_physics_process_internal(false);
- setplay = -1;
- }
-
output_ready = false;
- stream_fade_in = false;
- stream_fade_out = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
@@ -224,6 +218,7 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
case ATTENUATION_LOGARITHMIC: {
att = -20 * Math::log(p_distance / unit_size + CMP_EPSILON);
} break;
+ case ATTENUATION_DISABLED: break;
default: {
ERR_PRINT("Unknown attenuation type");
break;
@@ -662,7 +657,6 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
void AudioStreamPlayer3D::play(float p_from_pos) {
if (stream_playback.is_valid()) {
- stream_stop = false;
active = true;
setplay = p_from_pos;
output_ready = false;
@@ -680,8 +674,9 @@ void AudioStreamPlayer3D::seek(float p_seconds) {
void AudioStreamPlayer3D::stop() {
if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade_out = true;
+ active = false;
+ set_physics_process_internal(false);
+ setplay = -1;
}
}
@@ -831,7 +826,7 @@ float AudioStreamPlayer3D::get_attenuation_filter_db() const {
}
void AudioStreamPlayer3D::set_attenuation_model(AttenuationModel p_model) {
- ERR_FAIL_INDEX(p_model, 3);
+ ERR_FAIL_INDEX((int)p_model, 4);
attenuation_model = p_model;
}
@@ -877,8 +872,8 @@ void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade_in = stream_paused ? false : true;
- stream_fade_out = stream_paused ? true : false;
+ stream_paused_fade_in = stream_paused ? false : true;
+ stream_paused_fade_out = stream_paused ? true : false;
}
}
@@ -887,6 +882,10 @@ bool AudioStreamPlayer3D::get_stream_paused() const {
return stream_paused;
}
+Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
+ return stream_playback;
+}
+
void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
@@ -953,10 +952,12 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
+
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log"), "set_attenuation_model", "get_attenuation_model");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log,Disabled"), "set_attenuation_model", "get_attenuation_model");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db");
@@ -981,6 +982,7 @@ void AudioStreamPlayer3D::_bind_methods() {
BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_DISTANCE);
BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_SQUARE_DISTANCE);
BIND_ENUM_CONSTANT(ATTENUATION_LOGARITHMIC);
+ BIND_ENUM_CONSTANT(ATTENUATION_DISABLED);
BIND_ENUM_CONSTANT(OUT_OF_RANGE_MIX);
BIND_ENUM_CONSTANT(OUT_OF_RANGE_PAUSE);
@@ -1016,9 +1018,8 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() {
out_of_range_mode = OUT_OF_RANGE_MIX;
doppler_tracking = DOPPLER_TRACKING_DISABLED;
stream_paused = false;
- stream_fade_in = false;
- stream_fade_out = false;
- stream_stop = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
velocity_tracker.instance();
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index e467c170fb..98bc74b2e4 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -46,6 +46,7 @@ public:
ATTENUATION_INVERSE_DISTANCE,
ATTENUATION_INVERSE_SQUARE_DISTANCE,
ATTENUATION_LOGARITHMIC,
+ ATTENUATION_DISABLED,
};
enum OutOfRangeMode {
@@ -109,9 +110,8 @@ private:
float pitch_scale;
bool autoplay;
bool stream_paused;
- bool stream_fade_in;
- bool stream_fade_out;
- bool stream_stop;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -206,6 +206,8 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ Ref<AudioStreamPlayback> get_stream_playback();
+
AudioStreamPlayer3D();
~AudioStreamPlayer3D();
};
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 368cebeeab..a00f2173c0 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -55,16 +55,23 @@ void Camera::_update_camera_mode() {
case PROJECTION_ORTHOGONAL: {
set_orthogonal(size, near, far);
} break;
+ case PROJECTION_FRUSTUM: {
+ set_frustum(size, frustum_offset, near, far);
+ } break;
}
}
void Camera::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "fov") {
- if (mode == PROJECTION_ORTHOGONAL) {
+ if (mode != PROJECTION_PERSPECTIVE) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
} else if (p_property.name == "size") {
- if (mode == PROJECTION_PERSPECTIVE) {
+ if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) {
+ p_property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+ } else if (p_property.name == "frustum_offset") {
+ if (mode != PROJECTION_FRUSTUM) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
@@ -99,9 +106,15 @@ void Camera::_notification(int p_what) {
case NOTIFICATION_ENTER_WORLD: {
- bool first_camera = get_viewport()->_camera_add(this);
- if (!get_tree()->is_node_being_edited(this) && (current || first_camera))
- make_current();
+ // Needs to track the Viewport because it's needed on NOTIFICATION_EXIT_WORLD
+ // and Spatial will handle it first, including clearing its reference to the Viewport,
+ // therefore making it impossible to subclasses to access it
+ viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+
+ bool first_camera = viewport->_camera_add(this);
+ if (current || first_camera)
+ viewport->_camera_set(this);
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -123,17 +136,20 @@ void Camera::_notification(int p_what) {
}
}
- get_viewport()->_camera_remove(this);
+ if (viewport) {
+ viewport->_camera_remove(this);
+ viewport = NULL;
+ }
} break;
case NOTIFICATION_BECAME_CURRENT: {
- if (get_world().is_valid()) {
- get_world()->_register_camera(this);
+ if (viewport) {
+ viewport->find_world()->_register_camera(this);
}
} break;
case NOTIFICATION_LOST_CURRENT: {
- if (get_world().is_valid()) {
- get_world()->_remove_camera(this);
+ if (viewport) {
+ viewport->find_world()->_remove_camera(this);
}
} break;
}
@@ -177,8 +193,24 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) {
update_gizmo();
}
+void Camera::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) {
+ if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM)
+ return;
+
+ size = p_size;
+ frustum_offset = p_offset;
+
+ near = p_z_near;
+ far = p_z_far;
+ mode = PROJECTION_FRUSTUM;
+ force_change = false;
+
+ VisualServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far);
+ update_gizmo();
+}
+
void Camera::set_projection(Camera::Projection p_mode) {
- if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) {
+ if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
mode = p_mode;
_update_camera_mode();
_change_notify();
@@ -232,8 +264,6 @@ bool Camera::is_current() const {
return get_viewport()->get_camera() == this;
} else
return current;
-
- return false;
}
bool Camera::_can_gizmo_scale() const {
@@ -368,13 +398,17 @@ Point2 Camera::unproject_position(const Vector3 &p_pos) const {
return res;
}
-Vector3 Camera::project_position(const Point2 &p_point) const {
+Vector3 Camera::project_position(const Point2 &p_point, float p_z_depth) const {
if (!is_inside_tree()) {
ERR_EXPLAIN("Camera is not inside scene.");
ERR_FAIL_COND_V(!is_inside_tree(), Vector3());
}
+ if (p_z_depth == 0) {
+ return get_global_transform().origin;
+ }
+
Size2 viewport_size = get_viewport()->get_visible_rect().size;
CameraMatrix cm;
@@ -392,7 +426,7 @@ Vector3 Camera::project_position(const Point2 &p_point) const {
point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0;
point *= vp_size;
- Vector3 p(point.x, point.y, -near);
+ Vector3 p(point.x, point.y, -p_z_depth);
return get_camera_transform().xform(p);
}
@@ -467,19 +501,22 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("project_ray_origin", "screen_point"), &Camera::project_ray_origin);
ClassDB::bind_method(D_METHOD("unproject_position", "world_point"), &Camera::unproject_position);
ClassDB::bind_method(D_METHOD("is_position_behind", "world_point"), &Camera::is_position_behind);
- ClassDB::bind_method(D_METHOD("project_position", "screen_point"), &Camera::project_position);
+ ClassDB::bind_method(D_METHOD("project_position", "screen_point", "z_depth"), &Camera::project_position, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective);
ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal);
+ ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera::set_frustum);
ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current);
ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current);
ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform);
ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov);
+ ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera::get_frustum_offset);
ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size);
ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar);
ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear);
ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov);
+ ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera::set_frustum_offset);
ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size);
ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar);
ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear);
@@ -498,6 +535,7 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera::set_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_frustum"), &Camera::get_frustum);
+ ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera::get_camera);
ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera::set_cull_mask_bit);
ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera::get_cull_mask_bit);
@@ -510,15 +548,17 @@ void Camera::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
+ BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM);
BIND_ENUM_CONSTANT(KEEP_WIDTH);
BIND_ENUM_CONSTANT(KEEP_HEIGHT);
@@ -543,6 +583,10 @@ float Camera::get_znear() const {
return near;
}
+Vector2 Camera::get_frustum_offset() const {
+ return frustum_offset;
+}
+
float Camera::get_zfar() const {
return far;
@@ -570,6 +614,11 @@ void Camera::set_znear(float p_znear) {
_update_camera_mode();
}
+void Camera::set_frustum_offset(Vector2 p_offset) {
+ frustum_offset = p_offset;
+ _update_camera_mode();
+}
+
void Camera::set_zfar(float p_zfar) {
far = p_zfar;
_update_camera_mode();
@@ -648,9 +697,11 @@ Camera::Camera() {
camera = VisualServer::get_singleton()->camera_create();
size = 1;
fov = 0;
+ frustum_offset = Vector2();
near = 0;
far = 0;
current = false;
+ viewport = NULL;
force_change = false;
mode = PROJECTION_PERSPECTIVE;
set_perspective(70.0, 0.05, 100.0);
@@ -684,8 +735,9 @@ void ClippedCamera::set_process_mode(ProcessMode p_mode) {
if (process_mode == p_mode) {
return;
}
- set_process_internal(p_mode == CLIP_PROCESS_IDLE);
- set_physics_process_internal(p_mode == CLIP_PROCESS_PHYSICS);
+ process_mode = p_mode;
+ set_process_internal(process_mode == CLIP_PROCESS_IDLE);
+ set_physics_process_internal(process_mode == CLIP_PROCESS_PHYSICS);
}
ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const {
return process_mode;
@@ -748,7 +800,7 @@ void ClippedCamera::_notification(int p_what) {
float csafe, cunsafe;
if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, csafe, cunsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) {
- clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from).normalized() * csafe);
+ clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * csafe);
}
_update_camera();
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index a531324a85..1cd729199d 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -46,7 +46,8 @@ public:
enum Projection {
PROJECTION_PERSPECTIVE,
- PROJECTION_ORTHOGONAL
+ PROJECTION_ORTHOGONAL,
+ PROJECTION_FRUSTUM
};
enum KeepAspect {
@@ -63,11 +64,13 @@ public:
private:
bool force_change;
bool current;
+ Viewport *viewport;
Projection mode;
float fov;
float size;
+ Vector2 frustum_offset;
float near, far;
float v_offset;
float h_offset;
@@ -110,6 +113,7 @@ public:
void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far);
void set_orthogonal(float p_size, float p_z_near, float p_z_far);
+ void set_frustum(float p_size, Vector2 p_offset, float p_near, float p_far);
void set_projection(Camera::Projection p_mode);
void make_current();
@@ -123,12 +127,15 @@ public:
float get_size() const;
float get_zfar() const;
float get_znear() const;
+ Vector2 get_frustum_offset() const;
+
Projection get_projection() const;
void set_fov(float p_fov);
void set_size(float p_size);
void set_zfar(float p_zfar);
void set_znear(float p_znear);
+ void set_frustum_offset(Vector2 p_offset);
virtual Transform get_camera_transform() const;
@@ -137,7 +144,7 @@ public:
virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const;
virtual Point2 unproject_position(const Vector3 &p_pos) const;
bool is_position_behind(const Vector3 &p_pos) const;
- virtual Vector3 project_position(const Point2 &p_point) const;
+ virtual Vector3 project_position(const Point2 &p_point, float p_z_depth = 0) const;
Vector<Vector3> get_near_plane_points() const;
diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp
index d8c2042c88..fc46cf5bdb 100644
--- a/scene/3d/collision_object.cpp
+++ b/scene/3d/collision_object.cpp
@@ -52,7 +52,7 @@ void CollisionObject::_notification(int p_what) {
_update_pickable();
//get space
- };
+ } break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -259,9 +259,9 @@ void CollisionObject::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape> &
s.index = total_subshapes;
s.shape = p_shape;
if (area) {
- PhysicsServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform);
+ PhysicsServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
} else {
- PhysicsServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform);
+ PhysicsServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
}
sd.shapes.push_back(s);
diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp
index ac33e2b714..219ea56681 100644
--- a/scene/3d/collision_shape.cpp
+++ b/scene/3d/collision_shape.cpp
@@ -65,7 +65,6 @@ void CollisionShape::make_convex_from_brothers() {
}
void CollisionShape::_update_in_shape_owner(bool p_xform_only) {
-
parent->shape_owner_set_transform(owner_id, get_transform());
if (p_xform_only)
return;
@@ -91,7 +90,7 @@ void CollisionShape::_notification(int p_what) {
_update_in_shape_owner();
}
if (get_tree()->is_debugging_collisions_hint()) {
- _create_debug_shape();
+ _update_debug_shape();
}
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
@@ -142,17 +141,24 @@ void CollisionShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_convex_from_brothers"), &CollisionShape::make_convex_from_brothers);
ClassDB::set_method_flags("CollisionShape", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+ ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape::_shape_changed);
+ ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape::_update_debug_shape);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
}
void CollisionShape::set_shape(const Ref<Shape> &p_shape) {
- if (!shape.is_null())
+ if (!shape.is_null()) {
shape->unregister_owner(this);
+ shape->disconnect("changed", this, "_shape_changed");
+ }
shape = p_shape;
- if (!shape.is_null())
+ if (!shape.is_null()) {
shape->register_owner(this);
+ shape->connect("changed", this, "_shape_changed");
+ }
update_gizmo();
if (parent) {
parent->shape_owner_clear_shapes(owner_id);
@@ -161,6 +167,8 @@ void CollisionShape::set_shape(const Ref<Shape> &p_shape) {
}
}
+ if (is_inside_tree())
+ _shape_changed();
update_configuration_warning();
}
@@ -199,7 +207,8 @@ CollisionShape::~CollisionShape() {
//VisualServer::get_singleton()->free(indicator);
}
-void CollisionShape::_create_debug_shape() {
+void CollisionShape::_update_debug_shape() {
+ debug_shape_dirty = false;
if (debug_shape) {
debug_shape->queue_delete();
@@ -207,15 +216,24 @@ void CollisionShape::_create_debug_shape() {
}
Ref<Shape> s = get_shape();
-
if (s.is_null())
return;
Ref<Mesh> mesh = s->get_debug_mesh();
-
MeshInstance *mi = memnew(MeshInstance);
mi->set_mesh(mesh);
-
add_child(mi);
debug_shape = mi;
}
+
+void CollisionShape::_shape_changed() {
+ // If this is a heightfield shape our center may have changed
+ if (parent) {
+ _update_in_shape_owner(true);
+ }
+
+ if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) {
+ debug_shape_dirty = true;
+ call_deferred("_update_debug_shape");
+ }
+}
diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h
index 0c8e383a7f..98427b8590 100644
--- a/scene/3d/collision_shape.h
+++ b/scene/3d/collision_shape.h
@@ -45,12 +45,14 @@ class CollisionShape : public Spatial {
CollisionObject *parent;
Node *debug_shape;
+ bool debug_shape_dirty;
void resource_changed(RES res);
bool disabled;
protected:
- void _create_debug_shape();
+ void _update_debug_shape();
+ void _shape_changed();
void _update_in_shape_owner(bool p_xform_only = false);
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index fc2e5c9b0d..138c446fea 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -47,7 +47,8 @@ PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const {
void CPUParticles::set_emitting(bool p_emitting) {
emitting = p_emitting;
- set_process_internal(true);
+ if (emitting)
+ set_process_internal(true);
}
void CPUParticles::set_amount(int p_amount) {
@@ -211,7 +212,7 @@ String CPUParticles::get_configuration_warning() const {
get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) {
if (warnings != String())
warnings += "\n";
- warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled.");
+ warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial whose Billboard Mode is set to \"Particle Billboard\".");
}
return warnings;
@@ -544,7 +545,7 @@ void CPUParticles::_particles_process(float p_delta) {
if (restart_time >= prev_time && restart_time < time) {
restart = true;
if (fractional_delta) {
- local_delta = (time - restart_time) * lifetime;
+ local_delta = time - restart_time;
}
}
@@ -552,13 +553,13 @@ void CPUParticles::_particles_process(float p_delta) {
if (restart_time >= prev_time) {
restart = true;
if (fractional_delta) {
- local_delta = (1.0 - restart_time + time) * lifetime;
+ local_delta = lifetime - restart_time + time;
}
} else if (restart_time < time) {
restart = true;
if (fractional_delta) {
- local_delta = (time - restart_time) * lifetime;
+ local_delta = time - restart_time;
}
}
}
@@ -1002,9 +1003,11 @@ void CPUParticles::_set_redraw(bool p_redraw) {
if (redraw) {
VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
+ VS::get_singleton()->multimesh_set_visible_instances(multimesh, -1);
} else {
VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false);
+ VS::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
}
#ifndef NO_THREADS
update_mutex->unlock();
@@ -1029,7 +1032,7 @@ void CPUParticles::_update_render_thread() {
void CPUParticles::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- _set_redraw(true);
+ set_process_internal(emitting);
}
if (p_what == NOTIFICATION_EXIT_TREE) {
@@ -1048,8 +1051,6 @@ void CPUParticles::_notification(int p_what) {
float delta = get_process_delta_time();
if (emitting) {
-
- _set_redraw(true);
inactive_time = 0;
} else {
inactive_time += delta;
@@ -1065,6 +1066,7 @@ void CPUParticles::_notification(int p_what) {
return;
}
}
+ _set_redraw(true);
bool processed = false;
@@ -1392,6 +1394,7 @@ CPUParticles::CPUParticles() {
redraw = false;
multimesh = VisualServer::get_singleton()->multimesh_create();
+ VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
set_base(multimesh);
set_emitting(true);
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index cf1af918f7..2377068ede 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -152,6 +152,7 @@ PoolVector<Face3> Light::get_faces(uint32_t p_usage_flags) const {
void Light::set_bake_mode(BakeMode p_mode) {
bake_mode = p_mode;
+ VS::get_singleton()->light_set_use_gi(light, p_mode != BAKE_DISABLED);
}
Light::BakeMode Light::get_bake_mode() const {
@@ -293,7 +294,8 @@ Light::Light(VisualServer::LightType p_type) {
case VS::LIGHT_DIRECTIONAL: light = VisualServer::get_singleton()->directional_light_create(); break;
case VS::LIGHT_OMNI: light = VisualServer::get_singleton()->omni_light_create(); break;
case VS::LIGHT_SPOT: light = VisualServer::get_singleton()->spot_light_create(); break;
- default: {};
+ default: {
+ };
}
VS::get_singleton()->instance_set_base(get_instance(), light);
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 848889155b..89072519d5 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -96,7 +96,7 @@ void MeshInstance::_get_property_list(List<PropertyInfo> *p_list) const {
ls.sort();
for (List<String>::Element *E = ls.front(); E; E = E->next()) {
- p_list->push_back(PropertyInfo(Variant::REAL, E->get(), PROPERTY_HINT_RANGE, "0,1,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, E->get(), PROPERTY_HINT_RANGE, "0,1,0.00001"));
}
if (mesh.is_valid()) {
diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp
index 5a3c8223ff..612d91c6e1 100644
--- a/scene/3d/navigation.cpp
+++ b/scene/3d/navigation.cpp
@@ -340,16 +340,12 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
};
Vector3 entry = Geometry::get_closest_point_to_segment(begin_poly->entry, edge);
- begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry);
+ begin_poly->edges[i].C->distance = begin_point.distance_to(entry);
begin_poly->edges[i].C->entry = entry;
#else
begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center);
#endif
open_list.push_back(begin_poly->edges[i].C);
-
- if (begin_poly->edges[i].C == end_poly) {
- found_route = true;
- }
}
}
@@ -370,28 +366,7 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
float cost = p->distance;
#ifdef USE_ENTRY_POINT
- int es = p->edges.size();
-
- float shortest_distance = 1e30;
-
- for (int i = 0; i < es; i++) {
- Polygon::Edge &e = p->edges.write[i];
-
- if (!e.C)
- continue;
-
- Vector3 edge[2] = {
- _get_vertex(p->edges[i].point),
- _get_vertex(p->edges[(i + 1) % es].point)
- };
-
- Vector3 edge_point = Geometry::get_closest_point_to_segment(p->entry, edge);
- float dist = p->entry.distance_to(edge_point);
- if (dist < shortest_distance)
- shortest_distance = dist;
- }
-
- cost += shortest_distance;
+ cost += p->entry.distance_to(end_point);
#else
cost += p->center.distance_to(end_point);
#endif
@@ -404,6 +379,12 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
Polygon *p = least_cost_poly->get();
//open the neighbours for search
+ if (p == end_poly) {
+ //oh my reached end! stop algorithm
+ found_route = true;
+ break;
+ }
+
for (int i = 0; i < p->edges.size(); i++) {
Polygon::Edge &e = p->edges.write[i];
@@ -411,7 +392,17 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
if (!e.C)
continue;
+#ifdef USE_ENTRY_POINT
+ Vector3 edge[2] = {
+ _get_vertex(p->edges[i].point),
+ _get_vertex(p->edges[(i + 1) % p->edges.size()].point)
+ };
+
+ Vector3 entry = Geometry::get_closest_point_to_segment(p->entry, edge);
+ float distance = p->entry.distance_to(entry) + p->distance;
+#else
float distance = p->center.distance_to(e.C->center) + p->distance;
+#endif
if (e.C->prev_edge != -1) {
//oh this was visited already, can we win the cost?
@@ -420,25 +411,22 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
e.C->prev_edge = e.C_edge;
e.C->distance = distance;
+#ifdef USE_ENTRY_POINT
+ e.C->entry = entry;
+#endif
}
} else {
//add to open neighbours
e.C->prev_edge = e.C_edge;
e.C->distance = distance;
+#ifdef USE_ENTRY_POINT
+ e.C->entry = entry;
+#endif
open_list.push_back(e.C);
-
- if (e.C == end_poly) {
- //oh my reached end! stop algorithm
- found_route = true;
- break;
- }
}
}
- if (found_route)
- break;
-
open_list.erase(least_cost_poly);
}
@@ -539,8 +527,12 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
path.push_back(end_point);
while (true) {
int prev = p->prev_edge;
+#ifdef USE_ENTRY_POINT
+ Vector3 point = p->entry;
+#else
int prev_n = (p->prev_edge + 1) % p->edges.size();
Vector3 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5;
+#endif
path.push_back(point);
p = p->edges[prev].C;
if (p == begin_poly)
diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp
index 93731c4023..f82543b789 100644
--- a/scene/3d/navigation_mesh.cpp
+++ b/scene/3d/navigation_mesh.cpp
@@ -73,6 +73,41 @@ int NavigationMesh::get_sample_partition_type() const {
return static_cast<int>(partition_type);
}
+void NavigationMesh::set_parsed_geometry_type(int p_value) {
+ ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX);
+ parsed_geometry_type = static_cast<ParsedGeometryType>(p_value);
+ _change_notify();
+}
+
+int NavigationMesh::get_parsed_geometry_type() const {
+ return parsed_geometry_type;
+}
+
+void NavigationMesh::set_collision_mask(uint32_t p_mask) {
+
+ collision_mask = p_mask;
+}
+
+uint32_t NavigationMesh::get_collision_mask() const {
+
+ return collision_mask;
+}
+
+void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask |= 1 << p_bit;
+ else
+ mask &= ~(1 << p_bit);
+ set_collision_mask(mask);
+}
+
+bool NavigationMesh::get_collision_mask_bit(int p_bit) const {
+
+ return get_collision_mask() & (1 << p_bit);
+}
+
void NavigationMesh::set_cell_size(float p_value) {
cell_size = p_value;
}
@@ -204,6 +239,7 @@ bool NavigationMesh::get_filter_walkable_low_height_spans() const {
void NavigationMesh::set_vertices(const PoolVector<Vector3> &p_vertices) {
vertices = p_vertices;
+ _change_notify();
}
PoolVector<Vector3> NavigationMesh::get_vertices() const {
@@ -217,6 +253,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) {
for (int i = 0; i < p_array.size(); i++) {
polygons.write[i].indices = p_array[i];
}
+ _change_notify();
}
Array NavigationMesh::_get_polygons() const {
@@ -235,6 +272,7 @@ void NavigationMesh::add_polygon(const Vector<int> &p_polygon) {
Polygon polygon;
polygon.indices = p_polygon;
polygons.push_back(polygon);
+ _change_notify();
}
int NavigationMesh::get_polygon_count() const {
@@ -340,6 +378,15 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type);
ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationMesh::get_sample_partition_type);
+ ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationMesh::set_parsed_geometry_type);
+ ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationMesh::get_parsed_geometry_type);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit);
+
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationMesh::set_cell_size);
ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationMesh::get_cell_size);
@@ -405,30 +452,45 @@ void NavigationMesh::_bind_methods() {
BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE);
BIND_CONSTANT(SAMPLE_PARTITION_LAYERS);
+ BIND_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
+ BIND_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
+ BIND_CONSTANT(PARSED_GEOMETRY_BOTH);
+
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type");
-
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01"), "set_cell_size", "get_cell_size");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01"), "set_cell_height", "get_cell_height");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/height", PROPERTY_HINT_RANGE, "0.1,5.0,0.01"), "set_agent_height", "get_agent_height");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/radius", PROPERTY_HINT_RANGE, "0.1,5.0,0.01"), "set_agent_radius", "get_agent_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/max_climb", PROPERTY_HINT_RANGE, "0.1,5.0,0.01"), "set_agent_max_climb", "get_agent_max_climb");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_height", "get_cell_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/height", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_height", "get_agent_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/radius", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_radius", "get_agent_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/max_climb", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_max_climb", "get_agent_max_climb");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/max_slope", PROPERTY_HINT_RANGE, "0.0,90.0,0.1"), "set_agent_max_slope", "get_agent_max_slope");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/min_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01"), "set_region_min_size", "get_region_min_size");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/merge_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01"), "set_region_merge_size", "get_region_merge_size");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01"), "set_edge_max_length", "get_edge_max_length");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01"), "set_edge_max_error", "get_edge_max_error");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0"), "set_verts_per_poly", "get_verts_per_poly");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_detail_sample_distance", "get_detail_sample_distance");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_detail_sample_max_error", "get_detail_sample_max_error");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/min_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_min_size", "get_region_min_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/merge_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_merge_size", "get_region_merge_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater"), "set_edge_max_length", "get_edge_max_length");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater"), "set_edge_max_error", "get_edge_max_error");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_max_error", "get_detail_sample_max_error");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans");
}
+void NavigationMesh::_validate_property(PropertyInfo &property) const {
+ if (property.name == "geometry/collision_mask") {
+ if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) {
+ property.usage = 0;
+ return;
+ }
+ }
+}
+
NavigationMesh::NavigationMesh() {
cell_size = 0.3f;
cell_height = 0.2f;
@@ -445,7 +507,8 @@ NavigationMesh::NavigationMesh() {
detail_sample_max_error = 1.0f;
partition_type = SAMPLE_PARTITION_WATERSHED;
-
+ parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;
+ collision_mask = 0xFFFFFFFF;
filter_low_hanging_obstacles = false;
filter_ledge_spans = false;
filter_walkable_low_height_spans = false;
@@ -566,8 +629,17 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_na
navigation->navmesh_remove(nav_id);
nav_id = -1;
}
+
+ if (navmesh.is_valid()) {
+ navmesh->remove_change_receptor(this);
+ }
+
navmesh = p_navmesh;
+ if (navmesh.is_valid()) {
+ navmesh->add_change_receptor(this);
+ }
+
if (navigation && navmesh.is_valid() && enabled) {
nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
}
@@ -617,6 +689,11 @@ void NavigationMeshInstance::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
}
+void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) {
+ update_gizmo();
+ update_configuration_warning();
+}
+
NavigationMeshInstance::NavigationMeshInstance() {
debug_view = NULL;
@@ -625,3 +702,8 @@ NavigationMeshInstance::NavigationMeshInstance() {
enabled = true;
set_notify_transform(true);
}
+
+NavigationMeshInstance::~NavigationMeshInstance() {
+ if (navmesh.is_valid())
+ navmesh->remove_change_receptor(this);
+}
diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h
index 74531e2423..5fbf3998ff 100644
--- a/scene/3d/navigation_mesh.h
+++ b/scene/3d/navigation_mesh.h
@@ -57,6 +57,7 @@ class NavigationMesh : public Resource {
protected:
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const;
void _set_polygons(const Array &p_array);
Array _get_polygons() const;
@@ -69,6 +70,13 @@ public:
SAMPLE_PARTITION_MAX
};
+ enum ParsedGeometryType {
+ PARSED_GEOMETRY_MESH_INSTANCES = 0,
+ PARSED_GEOMETRY_STATIC_COLLIDERS,
+ PARSED_GEOMETRY_BOTH,
+ PARSED_GEOMETRY_MAX
+ };
+
protected:
float cell_size;
float cell_height;
@@ -85,6 +93,8 @@ protected:
float detail_sample_max_error;
SamplePartitionType partition_type;
+ ParsedGeometryType parsed_geometry_type;
+ uint32_t collision_mask;
bool filter_low_hanging_obstacles;
bool filter_ledge_spans;
@@ -95,6 +105,15 @@ public:
void set_sample_partition_type(int p_value);
int get_sample_partition_type() const;
+ void set_parsed_geometry_type(int p_value);
+ int get_parsed_geometry_type() const;
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
void set_cell_size(float p_value);
float get_cell_size() const;
@@ -174,6 +193,7 @@ class NavigationMeshInstance : public Spatial {
protected:
void _notification(int p_what);
static void _bind_methods();
+ void _changed_callback(Object *p_changed, const char *p_prop);
public:
void set_enabled(bool p_enabled);
@@ -185,6 +205,7 @@ public:
String get_configuration_warning() const;
NavigationMeshInstance();
+ ~NavigationMeshInstance();
};
#endif // NAVIGATION_MESH_H
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 57ab01f7be..156560f802 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -268,7 +268,7 @@ String Particles::get_configuration_warning() const {
process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
if (warnings != String())
warnings += "\n";
- warnings += "- " + TTR("Particles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled.");
+ warnings += "- " + TTR("Particles animation requires the usage of a SpatialMaterial whose Billboard Mode is set to \"Particle Billboard\".");
}
}
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index 190967d76c..84078911cb 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -173,7 +173,7 @@ void PathFollow::_update_transform() {
float dot = t_prev.dot(t_cur);
float angle = Math::acos(CLAMP(dot, -1, 1));
- if (likely(Math::abs(angle) > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(angle))) {
if (rotation_mode == ROTATION_Y) {
// assuming we're referring to global Y-axis. is this correct?
axis.x = 0;
@@ -184,7 +184,7 @@ void PathFollow::_update_transform() {
// all components are allowed
}
- if (likely(axis.length() > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(axis.length()))) {
t.rotate_basis(axis.normalized(), angle);
}
}
@@ -193,7 +193,7 @@ void PathFollow::_update_transform() {
float tilt_angle = c->interpolate_baked_tilt(o);
Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct??
- if (likely(Math::abs(tilt_angle) > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) {
if (rotation_mode == ROTATION_Y) {
tilt_axis.x = 0;
tilt_axis.z = 0;
@@ -203,7 +203,7 @@ void PathFollow::_update_transform() {
// all components are allowed
}
- if (likely(tilt_axis.length() > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(tilt_axis.length()))) {
t.rotate_basis(tilt_axis.normalized(), tilt_angle);
}
}
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 05214ed669..57af951110 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -1181,19 +1181,16 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
while (p_max_slides) {
Collision collision;
-
bool found_collision = false;
- int test_type = 0;
-
- do {
+ for (int i = 0; i < 2; ++i) {
bool collided;
- if (test_type == 0) { //collide
+ if (i == 0) { //collide
collided = move_and_collide(motion, p_infinite_inertia, collision);
if (!collided) {
motion = Vector3(); //clear because no collision happened and motion completed
}
- } else {
+ } else { //separate raycasts (if any)
collided = separate_raycast_shapes(p_infinite_inertia, collision);
if (collided) {
collision.remainder = motion; //keep
@@ -1203,9 +1200,6 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
if (collided) {
found_collision = true;
- }
-
- if (collided) {
colliders.push_back(collision);
motion = collision.remainder;
@@ -1222,7 +1216,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
floor_velocity = collision.collider_vel;
if (p_stop_on_slope) {
- if ((lv_n + p_floor_direction).length() < 0.01) {
+ if ((lv_n + p_floor_direction).length() < 0.01 && collision.travel.length() < 1) {
Transform gt = get_global_transform();
gt.origin -= collision.travel;
set_global_transform(gt);
@@ -1243,21 +1237,18 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
motion = motion.slide(p_floor_direction);
lv = lv.slide(p_floor_direction);
} else {
-
Vector3 n = collision.normal;
motion = motion.slide(n);
lv = lv.slide(n);
}
- for (int i = 0; i < 3; i++) {
- if (locked_axis & (1 << i)) {
- lv[i] = 0;
+ for (int j = 0; j < 3; j++) {
+ if (locked_axis & (1 << j)) {
+ lv[j] = 0;
}
}
}
-
- ++test_type;
- } while (!p_stop_on_slope && test_type < 2);
+ }
if (!found_collision || motion == Vector3())
break;
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index 589af98062..aa6030d44e 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -317,7 +317,7 @@ protected:
static void _bind_methods();
public:
- bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
+ bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia);
bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision);
diff --git a/scene/3d/proximity_group.cpp b/scene/3d/proximity_group.cpp
index 12eab2e4e8..96dc3304f2 100644
--- a/scene/3d/proximity_group.cpp
+++ b/scene/3d/proximity_group.cpp
@@ -204,6 +204,7 @@ ProximityGroup::ProximityGroup() {
group_version = 0;
dispatch_mode = MODE_PROXY;
+ cell_size = 1.0;
grid_radius = Vector3(1, 1, 1);
set_notify_transform(true);
};
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index b7279e4d4f..e192e040f2 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -162,7 +162,7 @@ void Skeleton::_update_process_order() {
//now check process order
int pass_count = 0;
while (pass_count < len * len) {
- //using bubblesort because of simplicity, it wont run every frame though.
+ //using bubblesort because of simplicity, it won't run every frame though.
//bublesort worst case is O(n^2), and this may be an infinite loop if cyclic
bool swapped = false;
for (int i = 0; i < len; i++) {
@@ -540,10 +540,11 @@ void Skeleton::clear_bones() {
void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) {
ERR_FAIL_INDEX(p_bone, bones.size());
- ERR_FAIL_COND(!is_inside_tree());
bones.write[p_bone].pose = p_pose;
- _make_dirty();
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
}
Transform Skeleton::get_bone_pose(int p_bone) const {
@@ -772,6 +773,8 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton::get_bone_rest);
ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton::set_bone_rest);
+ ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton::localize_rests);
+
ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton::set_bone_disable_rest);
ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton::is_bone_rest_disabled);
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
index ac20609c21..909d4fda34 100644
--- a/scene/3d/soft_body.cpp
+++ b/scene/3d/soft_body.cpp
@@ -104,6 +104,14 @@ SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
offset = obj_tocopy.offset;
}
+SoftBody::PinnedPoint SoftBody::PinnedPoint::operator=(const PinnedPoint &obj) {
+ point_index = obj.point_index;
+ spatial_attachment_path = obj.spatial_attachment_path;
+ spatial_attachment = obj.spatial_attachment;
+ offset = obj.offset;
+ return *this;
+}
+
void SoftBody::_update_pickable() {
if (!is_inside_tree())
return;
@@ -397,7 +405,7 @@ String SoftBody::get_configuration_warning() const {
if (!warning.empty())
warning += "\n\n";
- warning += TTR("This body will be ignored until you set a mesh");
+ warning += TTR("This body will be ignored until you set a mesh.");
}
Transform t = get_transform();
diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h
index 2516d39552..ee455f8dab 100644
--- a/scene/3d/soft_body.h
+++ b/scene/3d/soft_body.h
@@ -75,6 +75,7 @@ public:
PinnedPoint();
PinnedPoint(const PinnedPoint &obj_tocopy);
+ PinnedPoint operator=(const PinnedPoint &obj);
};
private:
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index 83f99a2e3c..efd418e3c7 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -224,7 +224,8 @@ void Spatial::_notification(int p_what) {
#endif
} break;
- default: {}
+ default: {
+ }
}
}
@@ -675,26 +676,29 @@ void Spatial::set_identity() {
void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up) {
- Transform lookat;
- lookat.origin = get_global_transform().origin;
- if (lookat.origin == p_target) {
+ Vector3 origin(get_global_transform().origin);
+ look_at_from_position(origin, p_target, p_up);
+}
+
+void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) {
+
+ if (p_pos == p_target) {
ERR_EXPLAIN("Node origin and target are in the same position, look_at() failed");
ERR_FAIL();
}
- if (p_up.cross(p_target - lookat.origin) == Vector3()) {
+ if (p_up.cross(p_target - p_pos) == Vector3()) {
ERR_EXPLAIN("Up vector and direction between node origin and target are aligned, look_at() failed");
ERR_FAIL();
}
- lookat = lookat.looking_at(p_target, p_up);
- set_global_transform(lookat);
-}
-
-void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) {
Transform lookat;
lookat.origin = p_pos;
+
+ Vector3 original_scale(get_global_transform().basis.get_scale());
lookat = lookat.looking_at(p_target, p_up);
+ // as basis was normalized, we just need to apply original scale back
+ lookat.basis.scale(original_scale);
set_global_transform(lookat);
}
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index c7f7b14a8f..32b8219ee0 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -270,6 +270,8 @@ void VehicleWheel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_skidinfo"), &VehicleWheel::get_skidinfo);
+ ClassDB::bind_method(D_METHOD("get_rpm"), &VehicleWheel::get_rpm);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering");
ADD_GROUP("Wheel", "wheel_");
@@ -311,6 +313,11 @@ float VehicleWheel::get_skidinfo() const {
return m_skidInfo;
}
+float VehicleWheel::get_rpm() const {
+
+ return m_rpm;
+}
+
VehicleWheel::VehicleWheel() {
steers = false;
@@ -716,7 +723,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
real_t rollingFriction = 0.f;
if (wheelInfo.m_raycastInfo.m_isInContact) {
- if (engine_force != 0.f) {
+ if (engine_force != 0.f && wheelInfo.engine_traction != false) {
rollingFriction = -engine_force * s->get_step();
} else {
real_t defaultRollingFrictionImpulse = 0.f;
@@ -865,12 +872,11 @@ void VehicleBody::_direct_state_changed(Object *p_state) {
real_t proj2 = fwd.dot(vel);
wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius);
- wheel.m_rotation += wheel.m_deltaRotation;
-
- } else {
- wheel.m_rotation += wheel.m_deltaRotation;
}
+ wheel.m_rotation += wheel.m_deltaRotation;
+ wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math_TAU;
+
wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact
}
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
index 7e7571df4d..9e3fe72282 100644
--- a/scene/3d/vehicle_body.h
+++ b/scene/3d/vehicle_body.h
@@ -68,6 +68,7 @@ class VehicleWheel : public Spatial {
real_t m_steering;
real_t m_rotation;
real_t m_deltaRotation;
+ real_t m_rpm;
real_t m_rollInfluence;
//real_t m_engineForce;
real_t m_brake;
@@ -134,6 +135,8 @@ public:
float get_skidinfo() const;
+ float get_rpm() const;
+
String get_configuration_warning() const;
VehicleWheel();
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
index 1bbf1b7bc7..99c86f0406 100644
--- a/scene/3d/visual_instance.cpp
+++ b/scene/3d/visual_instance.cpp
@@ -123,6 +123,8 @@ void VisualInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance::_get_visual_instance_rid);
ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance::set_base);
+ ClassDB::bind_method(D_METHOD("get_base"), &VisualInstance::get_base);
+ ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance::get_instance);
ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance::set_layer_mask);
ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance::get_layer_mask);
ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance::set_layer_mask_bit);
@@ -136,6 +138,12 @@ void VisualInstance::_bind_methods() {
void VisualInstance::set_base(const RID &p_base) {
VisualServer::get_singleton()->instance_set_base(instance, p_base);
+ base = p_base;
+}
+
+RID VisualInstance::get_base() const {
+
+ return base;
}
VisualInstance::VisualInstance() {
@@ -263,6 +271,11 @@ float GeometryInstance::get_extra_cull_margin() const {
return extra_cull_margin;
}
+void GeometryInstance::set_custom_aabb(AABB aabb) {
+
+ VS::get_singleton()->instance_set_custom_aabb(get_instance(), aabb);
+}
+
void GeometryInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance::set_material_override);
@@ -289,6 +302,8 @@ void GeometryInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance::set_extra_cull_margin);
ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance::get_extra_cull_margin);
+ ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance::set_custom_aabb);
+
ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance::get_aabb);
ADD_GROUP("Geometry", "");
diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h
index 3b6fccf65f..0e7d9be505 100644
--- a/scene/3d/visual_instance.h
+++ b/scene/3d/visual_instance.h
@@ -43,6 +43,7 @@ class VisualInstance : public Spatial {
GDCLASS(VisualInstance, Spatial);
OBJ_CATEGORY("3D Visual Nodes");
+ RID base;
RID instance;
uint32_t layers;
@@ -69,6 +70,7 @@ public:
virtual AABB get_transformed_aabb() const; // helper
void set_base(const RID &p_base);
+ RID get_base() const;
void set_layer_mask(uint32_t p_mask);
uint32_t get_layer_mask() const;
@@ -137,6 +139,8 @@ public:
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;
+ void set_custom_aabb(AABB aabb);
+
GeometryInstance();
};
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index 750ed97ae6..75b419ca58 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -835,7 +835,7 @@ void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const C
for (int i = 0; i < 3; i++) {
- if (ABS(light_axis[i]) < CMP_EPSILON)
+ if (Math::is_zero_approx(light_axis[i]))
continue;
clip[clip_planes].normal[i] = 1.0;
@@ -978,7 +978,7 @@ void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color
for (int c = 0; c < 3; c++) {
- if (ABS(light_axis[c]) < CMP_EPSILON)
+ if (Math::is_zero_approx(light_axis[c]))
continue;
clip[clip_planes].normal[c] = 1.0;
@@ -1113,7 +1113,7 @@ void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axi
for (int c = 0; c < 3; c++) {
- if (ABS(light_axis[c]) < CMP_EPSILON)
+ if (Math::is_zero_approx(light_axis[c]))
continue;
clip[clip_planes].normal[c] = 1.0;
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 0daa574f72..e9b38ae990 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -363,6 +363,7 @@ AnimationNodeOneShot::AnimationNodeOneShot() {
fade_out = 0.1;
autorestart = false;
autorestart_delay = 1;
+ autorestart_random_delay = 0;
mix = MIX_MODE_BLEND;
sync = false;
@@ -857,6 +858,7 @@ AnimationNodeTransition::AnimationNodeTransition() {
time = "time";
current = "current";
prev_current = "prev_current";
+ xfade = 0.0;
enabled_inputs = 0;
for (int i = 0; i < MAX_INPUTS; i++) {
diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp
index feefe3ddd4..84b3f103c5 100644
--- a/scene/animation/animation_cache.cpp
+++ b/scene/animation/animation_cache.cpp
@@ -309,7 +309,8 @@ void AnimationCache::set_all(float p_time, float p_delta) {
}
} break;
- default: {}
+ default: {
+ }
}
}
}
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 5df3da93e1..60f8806b25 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -314,8 +314,9 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm,
if (start_request_travel) {
if (!playing) {
+ String node_name = start_request;
start_request = StringName();
- ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active.");
+ ERR_EXPLAIN("Can't travel to '" + node_name + "' if state machine is not playing.");
ERR_FAIL_V(0);
}
@@ -440,13 +441,13 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm,
bool goto_next = false;
- if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE) {
- goto_next = fading_from == StringName();
- } else {
+ if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
goto_next = next_xfade >= (len_current - pos_current) || loops_current > 0;
if (loops_current > 0) {
next_xfade = 0;
}
+ } else {
+ goto_next = fading_from == StringName();
}
if (goto_next) { //loops should be used because fade time may be too small or zero and animation may have looped
@@ -960,7 +961,7 @@ void AnimationNodeStateMachine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node);
ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node);
- ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset);
+ ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset);
ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed);
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 016db15b73..75088c79fe 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -34,12 +34,23 @@
#include "core/message_queue.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
+
#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#include "scene/2d/skeleton_2d.h"
+
void AnimatedValuesBackup::update_skeletons() {
for (int i = 0; i < entries.size(); i++) {
if (entries[i].bone_idx != -1) {
+ // 3D bone
Object::cast_to<Skeleton>(entries[i].object)->notification(Skeleton::NOTIFICATION_UPDATE_SKELETON);
+ } else {
+ Bone2D *bone = Object::cast_to<Bone2D>(entries[i].object);
+ if (bone && bone->skeleton) {
+ // 2D bone
+ bone->skeleton->_update_transform();
+ }
}
}
}
@@ -1197,7 +1208,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float
// Animation reset BUT played backwards, set position to the end
c.current.pos = c.current.from->animation->get_length();
} else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) {
- // Animation resumed but already ended, set position to the beggining
+ // Animation resumed but already ended, set position to the beginning
c.current.pos = 0;
}
}
@@ -1510,13 +1521,19 @@ NodePath AnimationPlayer::get_root() const {
void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+#ifdef TOOLS_ENABLED
+ const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+#else
+ const String quote_style = "\"";
+#endif
+
String pf = p_function;
if (p_function == "play" || p_function == "play_backwards" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue") {
List<StringName> al;
get_animation_list(&al);
for (List<StringName>::Element *E = al.front(); E; E = E->next()) {
- r_options->push_back("\"" + String(E->get()) + "\"");
+ r_options->push_back(quote_style + String(E->get()) + quote_style);
}
}
Node::get_argument_options(p_function, p_idx, r_options);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 0b6fa26bfc..8247728b9d 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -883,16 +883,16 @@ void AnimationTree::_process_graph(float p_delta) {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (t->process_pass != process_pass) {
+ if (track->root_motion) {
- t->process_pass = process_pass;
- t->loc = Vector3();
- t->rot = Quat();
- t->rot_blend_accum = 0;
- t->scale = Vector3();
- }
+ if (t->process_pass != process_pass) {
- if (track->root_motion) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quat();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
float prev_time = time - delta;
if (prev_time < 0) {
@@ -946,7 +946,14 @@ void AnimationTree::_process_graph(float p_delta) {
Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
- scale -= Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes
+ if (t->process_pass != process_pass) {
+
+ t->process_pass = process_pass;
+ t->loc = loc;
+ t->rot = rot;
+ t->rot_blend_accum = 0;
+ t->scale = scale;
+ }
if (err != OK)
continue;
@@ -978,8 +985,7 @@ void AnimationTree::_process_graph(float p_delta) {
continue;
if (t->process_pass != process_pass) {
- Variant::CallError ce;
- t->value = Variant::construct(value.get_type(), NULL, 0, ce); //reset
+ t->value = value;
t->process_pass = process_pass;
}
@@ -1036,7 +1042,7 @@ void AnimationTree::_process_graph(float p_delta) {
float bezier = a->bezier_track_interpolate(i, time);
if (t->process_pass != process_pass) {
- t->value = 0;
+ t->value = bezier;
t->process_pass = process_pass;
}
@@ -1233,8 +1239,6 @@ void AnimationTree::_process_graph(float p_delta) {
Transform xform;
xform.origin = t->loc;
- t->scale += Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes and root motion
-
xform.basis.set_quat_scale(t->rot, t->scale);
if (t->root_motion) {
@@ -1268,7 +1272,8 @@ void AnimationTree::_process_graph(float p_delta) {
t->object->set_indexed(t->subpath, t->value);
} break;
- default: {} //the rest don't matter
+ default: {
+ } //the rest don't matter
}
}
}
@@ -1293,9 +1298,17 @@ void AnimationTree::_notification(int p_what) {
_clear_caches();
if (last_animation_player) {
- Object *old_player = ObjectDB::get_instance(last_animation_player);
- if (old_player) {
- old_player->disconnect("caches_cleared", this, "_clear_caches");
+ Object *player = ObjectDB::get_instance(last_animation_player);
+ if (player) {
+ player->disconnect("caches_cleared", this, "_clear_caches");
+ }
+ }
+ } else if (p_what == NOTIFICATION_ENTER_TREE) {
+ if (last_animation_player) {
+
+ Object *player = ObjectDB::get_instance(last_animation_player);
+ if (player) {
+ player->connect("caches_cleared", this, "_clear_caches");
}
}
}
@@ -1567,6 +1580,7 @@ AnimationTree::AnimationTree() {
active = false;
cache_valid = false;
setup_pass = 1;
+ process_pass = 1;
started = true;
properties_dirty = true;
last_animation_player = 0;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 3420b48ae3..3cc90c2ad6 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -193,7 +193,8 @@ bool AnimationTreePlayer::_set(const StringName &p_name, const Variant &p_value)
}
} break;
- default: {};
+ default: {
+ };
}
}
@@ -352,7 +353,8 @@ bool AnimationTreePlayer::_get(const StringName &p_name, Variant &r_ret) const {
node["transitions"] = transitions;
} break;
- default: {};
+ default: {
+ };
}
nodes.push_back(node);
@@ -772,7 +774,8 @@ float AnimationTreePlayer::_process_node(const StringName &p_node, AnimationNode
}
} break;
- default: {}
+ default: {
+ }
}
return 0;
@@ -882,7 +885,8 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]);
}
} break;
- default: {}
+ default: {
+ }
}
}
}
@@ -969,7 +973,8 @@ void AnimationTreePlayer::add_node(NodeType p_type, const StringName &p_node) {
n = memnew(TransitionNode);
} break;
- default: {}
+ default: {
+ }
}
//n->name+=" "+itos(p_node);
diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp
index 88fb2d5bfc..06d6806f03 100644
--- a/scene/animation/skeleton_ik.cpp
+++ b/scene/animation/skeleton_ik.cpp
@@ -37,20 +37,20 @@
#ifndef _3D_DISABLED
FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) {
- for (int i = childs.size() - 1; 0 <= i; --i) {
- if (p_bone_id == childs[i].bone) {
- return &childs.write[i];
+ for (int i = children.size() - 1; 0 <= i; --i) {
+ if (p_bone_id == children[i].bone) {
+ return &children.write[i];
}
}
return NULL;
}
FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) {
- const int infant_child_id = childs.size();
- childs.resize(infant_child_id + 1);
- childs.write[infant_child_id].bone = p_bone_id;
- childs.write[infant_child_id].parent_item = this;
- return &childs.write[infant_child_id];
+ const int infant_child_id = children.size();
+ children.resize(infant_child_id + 1);
+ children.write[infant_child_id].bone = p_bone_id;
+ children.write[infant_child_id].parent_item = this;
+ return &children.write[infant_child_id];
}
/// Build a chain that starts from the root to tip
@@ -144,8 +144,8 @@ void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_cha
p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone);
p_chain_item->current_pos = p_chain_item->initial_transform.origin;
- for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) {
- update_chain(p_sk, &p_chain_item->childs.write[i]);
+ for (int i = p_chain_item->children.size() - 1; 0 <= i; --i) {
+ update_chain(p_sk, &p_chain_item->children.write[i]);
}
}
@@ -210,9 +210,9 @@ void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_
while (sub_chain_root) { // Reach the tip
sub_chain_root->current_pos = origin;
- if (!sub_chain_root->childs.empty()) {
+ if (!sub_chain_root->children.empty()) {
- ChainItem &child(sub_chain_root->childs.write[0]);
+ ChainItem &child(sub_chain_root->children.write[0]);
// Is not tip
// So calculate next origin location
@@ -302,10 +302,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
Transform new_bone_pose(ci->initial_transform);
new_bone_pose.origin = ci->current_pos;
- if (!ci->childs.empty()) {
+ if (!ci->children.empty()) {
/// Rotate basis
- const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized());
+ const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized());
const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized());
if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) {
@@ -322,8 +322,8 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose);
- if (!ci->childs.empty())
- ci = &ci->childs.write[0];
+ if (!ci->children.empty())
+ ci = &ci->children.write[0];
else
ci = NULL;
}
@@ -335,7 +335,7 @@ void SkeletonIK::_validate_property(PropertyInfo &property) const {
if (skeleton) {
- String names;
+ String names("--,");
for (int i = 0; i < skeleton->get_bone_count(); i++) {
if (i > 0)
names += ",";
diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h
index 228184a2df..5d48b7be33 100644
--- a/scene/animation/skeleton_ik.h
+++ b/scene/animation/skeleton_ik.h
@@ -49,7 +49,7 @@ class FabrikInverseKinematic {
struct ChainItem {
- Vector<ChainItem> childs;
+ Vector<ChainItem> children;
ChainItem *parent_item;
// Bone info
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 6a5d7839f4..23998183b8 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -223,6 +223,7 @@ void Tween::_bind_methods() {
ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key")));
ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value")));
ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key")));
+ ADD_SIGNAL(MethodInfo("tween_all_completed"));
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode");
@@ -628,8 +629,10 @@ void Tween::_tween_process(float p_delta) {
}
pending_update--;
- if (all_finished)
+ if (all_finished) {
set_active(false);
+ emit_signal("tween_all_completed");
+ }
}
void Tween::set_tween_process_mode(TweenProcessMode p_mode) {
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 2688041d18..2def9fe8fc 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -32,34 +32,10 @@
#include "core/engine.h"
-void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
+void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames, int p_amount) {
int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
- //get data
- AudioFrame *buffer = mix_buffer.ptrw();
- int buffer_size = mix_buffer.size();
-
- if (p_fadeout) {
- // Short fadeout ramp
- buffer_size = MIN(buffer_size, 128);
- }
-
- stream_playback->mix(buffer, pitch_scale, buffer_size);
-
- //multiply volume interpolating to avoid clicks if this changes
- float target_volume = p_fadeout ? -80.0 : volume_db;
- float vol = Math::db2linear(mix_volume_db);
- float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
-
- for (int i = 0; i < buffer_size; i++) {
- buffer[i] *= vol;
- vol += vol_inc;
- }
-
- //set volume for next mix
- mix_volume_db = target_volume;
-
AudioFrame *targets[4] = { NULL, NULL, NULL, NULL };
if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) {
@@ -83,42 +59,81 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
for (int c = 0; c < 4; c++) {
if (!targets[c])
break;
- for (int i = 0; i < buffer_size; i++) {
- targets[c][i] += buffer[i];
+ for (int i = 0; i < p_amount; i++) {
+ targets[c][i] += p_frames[i];
}
}
}
+void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
+
+ //get data
+ AudioFrame *buffer = mix_buffer.ptrw();
+ int buffer_size = mix_buffer.size();
+
+ if (p_fadeout) {
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
+ }
+
+ stream_playback->mix(buffer, pitch_scale, buffer_size);
+
+ //multiply volume interpolating to avoid clicks if this changes
+ float target_volume = p_fadeout ? -80.0 : volume_db;
+ float vol = Math::db2linear(mix_volume_db);
+ float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
+
+ for (int i = 0; i < buffer_size; i++) {
+ buffer[i] *= vol;
+ vol += vol_inc;
+ }
+
+ //set volume for next mix
+ mix_volume_db = target_volume;
+
+ _mix_to_bus(buffer, buffer_size);
+}
+
void AudioStreamPlayer::_mix_audio() {
+ if (use_fadeout) {
+ _mix_to_bus(fadeout_buffer.ptr(), fadeout_buffer.size());
+ use_fadeout = false;
+ }
+
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade)) {
+ (stream_paused && !stream_paused_fade)) {
return;
}
- if (stream_fade) {
- _mix_internal(true);
- stream_fade = false;
-
- if (stream_stop) {
- stream_playback->stop();
- active = false;
- set_process_internal(false);
+ if (stream_paused) {
+ if (stream_paused_fade) {
+ _mix_internal(true);
+ stream_paused_fade = false;
}
return;
}
- if (setseek >= 0.0) {
+ if (setstop) {
+ _mix_internal(true);
+ stream_playback->stop();
+ setstop = false;
+ }
+
+ if (setseek >= 0.0 && !stop_has_priority) {
if (stream_playback->is_playing()) {
//fade out to avoid pops
_mix_internal(true);
}
+
stream_playback->start(setseek);
setseek = -1.0; //reset seek
mix_volume_db = volume_db; //reset ramp
}
+ stop_has_priority = false;
+
_mix_internal(false);
}
@@ -162,6 +177,28 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
AudioServer::get_singleton()->lock();
+ if (active && 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
+
+ AudioFrame *buffer = fadeout_buffer.ptrw();
+ int buffer_size = fadeout_buffer.size();
+
+ stream_playback->mix(buffer, pitch_scale, buffer_size);
+
+ //multiply volume interpolating to avoid clicks if this changes
+ float target_volume = -80.0;
+ float vol = Math::db2linear(mix_volume_db);
+ float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
+
+ for (int i = 0; i < buffer_size; i++) {
+ buffer[i] *= vol;
+ vol += vol_inc;
+ }
+
+ use_fadeout = true;
+ }
+
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
if (stream_playback.is_valid()) {
@@ -169,6 +206,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
stream.unref();
active = false;
setseek = -1;
+ setstop = false;
}
if (p_stream.is_valid()) {
@@ -209,8 +247,8 @@ void AudioStreamPlayer::play(float p_from_pos) {
if (stream_playback.is_valid()) {
//mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks
- stream_stop = false;
setseek = p_from_pos;
+ stop_has_priority = false;
active = true;
set_process_internal(true);
}
@@ -225,16 +263,16 @@ void AudioStreamPlayer::seek(float p_seconds) {
void AudioStreamPlayer::stop() {
- if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade = true;
+ if (stream_playback.is_valid() && active) {
+ setstop = true;
+ stop_has_priority = true;
}
}
bool AudioStreamPlayer::is_playing() const {
if (stream_playback.is_valid()) {
- return active; //&& stream_playback->is_playing();
+ return active && !setstop; //&& stream_playback->is_playing();
}
return false;
@@ -301,7 +339,7 @@ void AudioStreamPlayer::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade = p_pause ? true : false;
+ stream_paused_fade = p_pause ? true : false;
}
}
@@ -331,6 +369,10 @@ void AudioStreamPlayer::_bus_layout_changed() {
_change_notify();
}
+Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
+ return stream_playback;
+}
+
void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream);
@@ -366,6 +408,8 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
@@ -391,9 +435,11 @@ AudioStreamPlayer::AudioStreamPlayer() {
setseek = -1;
active = false;
stream_paused = false;
- stream_fade = false;
- stream_stop = false;
+ stream_paused_fade = false;
mix_target = MIX_TARGET_STEREO;
+ fadeout_buffer.resize(512);
+ setstop = false;
+ use_fadeout = false;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index fba8ce7dd3..7987bd7e7d 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -49,17 +49,20 @@ private:
Ref<AudioStreamPlayback> stream_playback;
Ref<AudioStream> stream;
Vector<AudioFrame> mix_buffer;
+ Vector<AudioFrame> fadeout_buffer;
+ bool use_fadeout;
volatile float setseek;
volatile bool active;
+ volatile bool setstop;
+ volatile bool stop_has_priority;
float mix_volume_db;
float pitch_scale;
float volume_db;
bool autoplay;
bool stream_paused;
- bool stream_fade;
- bool stream_stop;
+ bool stream_paused_fade;
StringName bus;
MixTarget mix_target;
@@ -72,6 +75,7 @@ private:
bool _is_active() const;
void _bus_layout_changed();
+ void _mix_to_bus(const AudioFrame *p_frames, int p_amount);
protected:
void _validate_property(PropertyInfo &property) const;
@@ -106,6 +110,8 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ Ref<AudioStreamPlayback> get_stream_playback();
+
AudioStreamPlayer();
~AudioStreamPlayer();
};
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 806c8afa5b..5ef2557383 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -56,185 +56,53 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) {
if (status.disabled) // no interaction with disabled button
return;
- Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseButton> mouse_button = p_event;
+ bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo();
- if (b.is_valid()) {
- if (status.disabled || ((1 << (b->get_button_index() - 1)) & button_mask) == 0)
- return;
-
- if (status.pressing_button)
- return;
-
- if (action_mode == ACTION_MODE_BUTTON_PRESS) {
-
- if (b->is_pressed()) {
-
- emit_signal("button_down");
-
- if (!toggle_mode) { //mouse press attempt
-
- status.press_attempt = true;
- status.pressing_inside = true;
+ bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) > 0;
+ if (button_masked || ui_accept) {
+ if (p_event->is_pressed()) {
+ status.press_attempt = true;
+ status.pressing_inside = true;
+ emit_signal("button_down");
+ }
- pressed();
- if (get_script_instance()) {
- Variant::CallError ce;
- get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce);
+ if (status.press_attempt && status.pressing_inside) {
+ if (toggle_mode) {
+ if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
+ if (action_mode == ACTION_MODE_BUTTON_PRESS) {
+ status.press_attempt = false;
+ status.pressing_inside = false;
}
-
- _unpress_group();
- emit_signal("pressed");
-
- } else {
-
status.pressed = !status.pressed;
- pressed();
-
_unpress_group();
- emit_signal("pressed");
-
- toggled(status.pressed);
- if (get_script_instance()) {
- get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed);
- }
- emit_signal("toggled", status.pressed);
+ _toggled(status.pressed);
+ _pressed();
}
-
} else {
-
- emit_signal("button_up");
-
- /* this is pointless if (status.press_attempt && status.pressing_inside) {
- //released();
- emit_signal("released");
+ if (!p_event->is_pressed()) {
+ _pressed();
}
-*/
- status.press_attempt = false;
}
- update();
- return;
}
- if (b->is_pressed()) {
-
- status.press_attempt = true;
- status.pressing_inside = true;
- emit_signal("button_down");
-
- } else {
-
+ if (!p_event->is_pressed()) { // pressed state should be correct with button_up signal
emit_signal("button_up");
-
- if (status.press_attempt && status.pressing_inside) {
-
- if (!toggle_mode) { //mouse press attempt
-
- pressed();
- if (get_script_instance()) {
- Variant::CallError ce;
- get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce);
- }
-
- _unpress_group();
- emit_signal("pressed");
-
- } else {
-
- status.pressed = !status.pressed;
-
- pressed();
- _unpress_group();
- emit_signal("pressed");
-
- toggled(status.pressed);
- if (get_script_instance()) {
- get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed);
- }
- emit_signal("toggled", status.pressed);
- }
- }
-
status.press_attempt = false;
}
update();
+ return;
}
- Ref<InputEventMouseMotion> mm = p_event;
-
- if (mm.is_valid()) {
- if (status.press_attempt && status.pressing_button == 0) {
+ Ref<InputEventMouseMotion> mouse_motion = p_event;
+ if (mouse_motion.is_valid()) {
+ if (status.press_attempt) {
bool last_press_inside = status.pressing_inside;
- status.pressing_inside = has_point(mm->get_position());
- if (last_press_inside != status.pressing_inside)
+ status.pressing_inside = has_point(mouse_motion->get_position());
+ if (last_press_inside != status.pressing_inside) {
update();
- }
- }
-
- if (!mm.is_valid() && !b.is_valid()) {
-
- if (p_event->is_echo()) {
- return;
- }
-
- if (status.disabled) {
- return;
- }
-
- if (status.press_attempt && status.pressing_button == 0) {
- return;
- }
-
- if (p_event->is_action("ui_accept")) {
-
- if (p_event->is_pressed()) {
-
- status.pressing_button++;
- status.press_attempt = true;
- status.pressing_inside = true;
- emit_signal("button_down");
-
- } else if (status.press_attempt) {
-
- if (status.pressing_button)
- status.pressing_button--;
-
- if (status.pressing_button)
- return;
-
- status.press_attempt = false;
- status.pressing_inside = false;
-
- emit_signal("button_up");
-
- if (!toggle_mode) { //mouse press attempt
-
- pressed();
- if (get_script_instance()) {
- Variant::CallError ce;
- get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce);
- }
-
- _unpress_group();
- emit_signal("pressed");
- } else {
-
- status.pressed = !status.pressed;
-
- pressed();
- _unpress_group();
- emit_signal("pressed");
-
- toggled(status.pressed);
- if (get_script_instance()) {
- get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed);
- }
- emit_signal("toggled", status.pressed);
- }
}
-
- accept_event();
- update();
}
}
}
@@ -255,7 +123,6 @@ void BaseButton::_notification(int p_what) {
if (status.press_attempt) {
status.press_attempt = false;
- status.pressing_button = 0;
update();
}
}
@@ -268,9 +135,8 @@ void BaseButton::_notification(int p_what) {
if (p_what == NOTIFICATION_FOCUS_EXIT) {
- if (status.pressing_button && status.press_attempt) {
+ if (status.press_attempt) {
status.press_attempt = false;
- status.pressing_button = 0;
status.hovering = false;
update();
} else if (status.hovering) {
@@ -282,10 +148,7 @@ void BaseButton::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
}
- if (p_what == NOTIFICATION_EXIT_TREE) {
- }
-
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree()) {
+ if (p_what == NOTIFICATION_EXIT_TREE || (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree())) {
if (!toggle_mode) {
status.pressed = false;
@@ -293,21 +156,31 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
status.press_attempt = false;
status.pressing_inside = false;
- status.pressing_button = 0;
}
}
-void BaseButton::pressed() {
+void BaseButton::_pressed() {
- if (get_script_instance())
- get_script_instance()->call("pressed");
+ if (get_script_instance()) {
+ get_script_instance()->call(SceneStringNames::get_singleton()->_pressed);
+ }
+ pressed();
+ emit_signal("pressed");
}
-void BaseButton::toggled(bool p_pressed) {
+void BaseButton::_toggled(bool p_pressed) {
if (get_script_instance()) {
- get_script_instance()->call("toggled", p_pressed);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, p_pressed);
}
+ toggled(p_pressed);
+ emit_signal("toggled", p_pressed);
+}
+
+void BaseButton::pressed() {
+}
+
+void BaseButton::toggled(bool p_pressed) {
}
void BaseButton::set_disabled(bool p_disabled) {
@@ -321,7 +194,6 @@ void BaseButton::set_disabled(bool p_disabled) {
}
status.press_attempt = false;
status.pressing_inside = false;
- status.pressing_button = 0;
}
update();
_change_notify("disabled");
@@ -344,6 +216,10 @@ void BaseButton::set_pressed(bool p_pressed) {
if (p_pressed) {
_unpress_group();
}
+ if (toggle_mode) {
+ _toggled(status.pressed);
+ }
+
update();
}
@@ -379,7 +255,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
bool pressing;
if (status.press_attempt) {
- pressing = status.pressing_inside;
+ pressing = (status.pressing_inside || keep_pressed_outside);
if (status.pressed)
pressing = !pressing;
} else {
@@ -449,6 +325,16 @@ Control::FocusMode BaseButton::get_enabled_focus_mode() const {
return enabled_focus_mode;
}
+void BaseButton::set_keep_pressed_outside(bool p_on) {
+
+ keep_pressed_outside = p_on;
+}
+
+bool BaseButton::is_keep_pressed_outside() const {
+
+ return keep_pressed_outside;
+}
+
void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
if (shortcut.is_null() == p_shortcut.is_null())
@@ -531,6 +417,8 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode);
ClassDB::bind_method(D_METHOD("set_enabled_focus_mode", "mode"), &BaseButton::set_enabled_focus_mode);
ClassDB::bind_method(D_METHOD("get_enabled_focus_mode"), &BaseButton::get_enabled_focus_mode);
+ ClassDB::bind_method(D_METHOD("set_keep_pressed_outside", "enabled"), &BaseButton::set_keep_pressed_outside);
+ ClassDB::bind_method(D_METHOD("is_keep_pressed_outside"), &BaseButton::is_keep_pressed_outside);
ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &BaseButton::set_shortcut);
ClassDB::bind_method(D_METHOD("get_shortcut"), &BaseButton::get_shortcut);
@@ -552,6 +440,7 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
@@ -569,12 +458,12 @@ BaseButton::BaseButton() {
toggle_mode = false;
shortcut_in_tooltip = true;
+ keep_pressed_outside = false;
status.pressed = false;
status.press_attempt = false;
status.hovering = false;
status.pressing_inside = false;
status.disabled = false;
- status.pressing_button = 0;
set_focus_mode(FOCUS_ALL);
enabled_focus_mode = FOCUS_ALL;
action_mode = ACTION_MODE_BUTTON_RELEASE;
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 9a00cc79f2..abb3f58d6b 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -52,6 +52,7 @@ private:
int button_mask;
bool toggle_mode;
bool shortcut_in_tooltip;
+ bool keep_pressed_outside;
FocusMode enabled_focus_mode;
Ref<ShortCut> shortcut;
@@ -64,13 +65,14 @@ private:
bool pressing_inside;
bool disabled;
- int pressing_button;
} status;
Ref<ButtonGroup> button_group;
void _unpress_group();
+ void _pressed();
+ void _toggled(bool p_pressed);
protected:
virtual void pressed();
@@ -110,6 +112,9 @@ public:
void set_action_mode(ActionMode p_mode);
ActionMode get_action_mode() const;
+ void set_keep_pressed_outside(bool p_on);
+ bool is_keep_pressed_outside() const;
+
void set_button_mask(int p_mask);
int get_button_mask() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index c734136895..65e9cccd05 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "button.h"
+
#include "core/translation.h"
#include "servers/visual_server.h"
@@ -102,6 +103,7 @@ void Button::_notification(int p_what) {
break;
}
+ FALLTHROUGH;
}
case DRAW_PRESSED: {
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index 35e3119473..a2d0f388c4 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -34,13 +34,15 @@
#include "servers/visual_server.h"
Size2 CheckButton::get_icon_size() const {
- Ref<Texture> on = Control::get_icon("on");
- Ref<Texture> off = Control::get_icon("off");
+
+ Ref<Texture> on = Control::get_icon(is_disabled() ? "on_disabled" : "on");
+ Ref<Texture> off = Control::get_icon(is_disabled() ? "off_disabled" : "off");
Size2 tex_size = Size2(0, 0);
if (!on.is_null())
tex_size = Size2(on->get_width(), on->get_height());
if (!off.is_null())
tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height()));
+
return tex_size;
}
@@ -49,9 +51,8 @@ Size2 CheckButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
Size2 tex_size = get_icon_size();
minsize.width += tex_size.width;
- if (get_text().length() > 0) {
+ if (get_text().length() > 0)
minsize.width += get_constant("hseparation");
- }
Ref<StyleBox> sb = get_stylebox("normal");
minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_BOTTOM));
@@ -67,8 +68,8 @@ void CheckButton::_notification(int p_what) {
RID ci = get_canvas_item();
- Ref<Texture> on = Control::get_icon("on");
- Ref<Texture> off = Control::get_icon("off");
+ Ref<Texture> on = Control::get_icon(is_disabled() ? "on_disabled" : "on");
+ Ref<Texture> off = Control::get_icon(is_disabled() ? "off_disabled" : "off");
Ref<StyleBox> sb = get_stylebox("normal");
Vector2 ofs;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 7aca6acd00..bca3471091 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -38,8 +38,6 @@
#include "editor_scale.h"
#include "editor_settings.h"
#endif
-
-#include "scene/gui/separator.h"
#include "scene/main/viewport.h"
void ColorPicker::_notification(int p_what) {
@@ -469,7 +467,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) {
+ } else if (bev->is_pressed() && bev->get_button_index() == BUTTON_RIGHT && presets_enabled) {
int index = bev->get_position().x / (preset->get_size().x / presets.size());
Color clicked_preset = presets[index];
erase_preset(clicked_preset);
@@ -565,6 +563,31 @@ void ColorPicker::_html_focus_exit() {
_focus_exit();
}
+void ColorPicker::set_presets_enabled(bool p_enabled) {
+ presets_enabled = p_enabled;
+ if (!p_enabled) {
+ bt_add_preset->set_disabled(true);
+ bt_add_preset->set_focus_mode(FOCUS_NONE);
+ } else {
+ bt_add_preset->set_disabled(false);
+ bt_add_preset->set_focus_mode(FOCUS_ALL);
+ }
+}
+
+bool ColorPicker::are_presets_enabled() const {
+ return presets_enabled;
+}
+
+void ColorPicker::set_presets_visible(bool p_visible) {
+ presets_visible = p_visible;
+ preset_separator->set_visible(p_visible);
+ preset_container->set_visible(p_visible);
+}
+
+bool ColorPicker::are_presets_visible() const {
+ return presets_visible;
+}
+
void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color);
@@ -575,6 +598,10 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha);
+ ClassDB::bind_method(D_METHOD("set_presets_enabled", "enabled"), &ColorPicker::set_presets_enabled);
+ ClassDB::bind_method(D_METHOD("are_presets_enabled"), &ColorPicker::are_presets_enabled);
+ ClassDB::bind_method(D_METHOD("set_presets_visible", "visible"), &ColorPicker::set_presets_visible);
+ ClassDB::bind_method(D_METHOD("are_presets_visible"), &ColorPicker::are_presets_visible);
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);
@@ -598,6 +625,8 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
+ 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")));
@@ -613,6 +642,8 @@ ColorPicker::ColorPicker() :
raw_mode_enabled = false;
deferred_mode_enabled = false;
changing_color = false;
+ presets_enabled = true;
+ presets_visible = true;
screen = NULL;
HBoxContainer *hb_smpl = memnew(HBoxContainer);
@@ -725,18 +756,19 @@ ColorPicker::ColorPicker() :
set_pick_color(Color(1, 1, 1));
- add_child(memnew(HSeparator));
+ preset_separator = memnew(HSeparator);
+ add_child(preset_separator);
- HBoxContainer *bbc = memnew(HBoxContainer);
- add_child(bbc);
+ preset_container = memnew(HBoxContainer);
+ add_child(preset_container);
preset = memnew(TextureRect);
- bbc->add_child(preset);
+ preset_container->add_child(preset);
preset->connect("gui_input", this, "_preset_input");
preset->connect("draw", this, "_update_presets");
bt_add_preset = memnew(Button);
- bbc->add_child(bt_add_preset);
+ preset_container->add_child(bt_add_preset);
bt_add_preset->set_tooltip(TTR("Add current color as a preset."));
bt_add_preset->connect("pressed", this, "_add_preset_pressed");
}
@@ -758,23 +790,33 @@ void ColorPickerButton::_modal_closed() {
void ColorPickerButton::pressed() {
_update_picker();
- popup->set_position(get_global_position() - picker->get_combined_minimum_size());
+ popup->set_position(get_global_position() - picker->get_combined_minimum_size() * get_global_transform().get_scale());
+ popup->set_scale(get_global_transform().get_scale());
popup->popup();
picker->set_focus_on_line_edit();
}
void ColorPickerButton::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
- Ref<StyleBox> normal = get_stylebox("normal");
- Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
- draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true);
- draw_rect(r, color);
+ Ref<StyleBox> normal = get_stylebox("normal");
+ Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
+ draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true);
+ draw_rect(r, color);
+ } break;
+ case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: {
+
+ if (popup)
+ popup->hide();
+ } break;
}
- if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST && popup) {
- popup->hide();
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (popup && !is_visible_in_tree()) {
+ popup->hide();
+ }
}
}
@@ -825,6 +867,8 @@ void ColorPickerButton::_update_picker() {
add_child(popup);
picker->connect("color_changed", this, "_color_changed");
popup->connect("modal_closed", this, "_modal_closed");
+ popup->connect("about_to_show", this, "set_pressed", varray(true));
+ popup->connect("popup_hide", this, "set_pressed", varray(false));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
}
@@ -855,4 +899,6 @@ ColorPickerButton::ColorPickerButton() {
picker = NULL;
popup = NULL;
edit_alpha = true;
+
+ set_toggle_mode(true);
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index b78844839a..b5ddf2d0c2 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -37,6 +37,7 @@
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/popup.h"
+#include "scene/gui/separator.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
@@ -52,6 +53,8 @@ private:
Control *w_edit;
TextureRect *sample;
TextureRect *preset;
+ HBoxContainer *preset_container;
+ HSeparator *preset_separator;
Button *bt_add_preset;
List<Color> presets;
ToolButton *btn_pick;
@@ -70,6 +73,8 @@ private:
bool deferred_mode_enabled;
bool updating;
bool changing_color;
+ bool presets_enabled;
+ bool presets_visible;
float h, s, v;
Color last_hsv;
@@ -114,6 +119,12 @@ public:
void set_deferred_mode(bool p_enabled);
bool is_deferred_mode() const;
+ void set_presets_enabled(bool p_enabled);
+ bool are_presets_enabled() const;
+
+ void set_presets_visible(bool p_visible);
+ bool are_presets_visible() const;
+
void set_focus_on_line_edit();
ColorPicker();
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 7f1ca58d58..1f9bfb9936 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -177,7 +177,7 @@ String Container::get_configuration_warning() const {
if (warning != String()) {
warning += "\n";
}
- warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you dont't intend to add a script, then please use a plain 'Control' node instead.");
+ warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you don't intend to add a script, then please use a plain 'Control' node instead.");
}
return warning;
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 86894ce070..76275c2420 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -43,6 +43,7 @@
#include "scene/scene_string_names.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
#endif
#include <stdio.h>
@@ -66,6 +67,7 @@ Dictionary Control::_edit_get_state() const {
s["margins"] = margins;
return s;
}
+
void Control::_edit_set_state(const Dictionary &p_state) {
Dictionary state = p_state;
@@ -87,7 +89,12 @@ void Control::_edit_set_state(const Dictionary &p_state) {
}
void Control::_edit_set_position(const Point2 &p_position) {
+#ifdef TOOLS_ENABLED
+ set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+#else
+ // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position(p_position);
+#endif
};
Point2 Control::_edit_get_position() const {
@@ -103,8 +110,14 @@ Size2 Control::_edit_get_scale() const {
}
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
+#ifdef TOOLS_ENABLED
+ set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+ set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+#else
+ // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
+#endif
}
Rect2 Control::_edit_get_rect() const {
@@ -276,7 +289,7 @@ void Control::_update_minimum_size() {
Size2 minsize = get_combined_minimum_size();
if (minsize.x > data.size_cache.x ||
minsize.y > data.size_cache.y) {
- _size_changed();
+ set_size(data.size_cache);
}
data.updating_last_minimum_size = false;
@@ -1366,7 +1379,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range;
float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range;
- data.anchor[p_margin] = CLAMP(p_anchor, 0.0, 1.0);
+ data.anchor[p_margin] = p_anchor;
if (((p_margin == MARGIN_LEFT || p_margin == MARGIN_TOP) && data.anchor[p_margin] > data.anchor[(p_margin + 2) % 4]) ||
((p_margin == MARGIN_RIGHT || p_margin == MARGIN_BOTTOM) && data.anchor[p_margin] < data.anchor[(p_margin + 2) % 4])) {
@@ -1388,19 +1401,14 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
update();
- _change_notify("anchor");
+ _change_notify("anchor_left");
+ _change_notify("anchor_right");
+ _change_notify("anchor_top");
+ _change_notify("anchor_bottom");
}
void Control::_set_anchor(Margin p_margin, float p_anchor) {
-#ifdef TOOLS_ENABLED
- if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {
- set_anchor(p_margin, p_anchor, EDITOR_DEF("editors/2d/keep_margins_when_changing_anchors", false));
- } else {
- set_anchor(p_margin, p_anchor, false);
- }
-#else
- set_anchor(p_margin, p_anchor, false);
-#endif
+ set_anchor(p_margin, p_anchor);
}
void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos, bool p_push_opposite_anchor) {
@@ -1409,7 +1417,7 @@ void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos
set_margin(p_margin, p_pos);
}
-void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
+void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) {
//Left
switch (p_preset) {
case PRESET_TOP_LEFT:
@@ -1420,21 +1428,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- set_anchor(MARGIN_LEFT, 0.5, p_keep_margin);
+ set_anchor(MARGIN_LEFT, 0.5, p_keep_margins);
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
- set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margins);
break;
}
@@ -1448,21 +1456,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- set_anchor(MARGIN_TOP, 0.5, p_keep_margin);
+ set_anchor(MARGIN_TOP, 0.5, p_keep_margins);
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
- set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margins);
break;
}
@@ -1472,14 +1480,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
- set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- set_anchor(MARGIN_RIGHT, 0.5, p_keep_margin);
+ set_anchor(MARGIN_RIGHT, 0.5, p_keep_margins);
break;
case PRESET_TOP_RIGHT:
@@ -1490,7 +1498,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margins);
break;
}
@@ -1500,14 +1508,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
- set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margin);
+ set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margins);
break;
case PRESET_BOTTOM_LEFT:
@@ -1518,7 +1526,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margins);
break;
}
}
@@ -1711,7 +1719,11 @@ Point2 Control::get_global_position() const {
return get_global_transform().get_origin();
}
-void Control::set_global_position(const Point2 &p_point) {
+void Control::_set_global_position(const Point2 &p_point) {
+ set_global_position(p_point);
+}
+
+void Control::set_global_position(const Point2 &p_point, bool p_keep_margins) {
Transform2D inv;
@@ -1720,7 +1732,7 @@ void Control::set_global_position(const Point2 &p_point) {
inv = data.parent_canvas_item->get_global_transform().affine_inverse();
}
- set_position(inv.xform(p_point));
+ set_position(inv.xform(p_point), p_keep_margins);
}
Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
@@ -1734,6 +1746,15 @@ Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margi
return result;
}
+void Control::_compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]) {
+
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ r_anchors[0] = (p_rect.position.x - p_margins[0]) / parent_rect_size.x;
+ r_anchors[1] = (p_rect.position.y - p_margins[1]) / parent_rect_size.y;
+ r_anchors[2] = (p_rect.position.x + p_rect.size.x - p_margins[2]) / parent_rect_size.x;
+ r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_margins[3]) / parent_rect_size.y;
+}
+
void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
@@ -1743,13 +1764,28 @@ void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r
r_margins[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
}
-void Control::set_position(const Size2 &p_point) {
+void Control::_set_position(const Size2 &p_point) {
+ set_position(p_point);
+}
- _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
+void Control::set_position(const Size2 &p_point, bool p_keep_margins) {
+ if (p_keep_margins) {
+ _compute_anchors(Rect2(p_point, data.size_cache), data.margin, data.anchor);
+ _change_notify("anchor_left");
+ _change_notify("anchor_right");
+ _change_notify("anchor_top");
+ _change_notify("anchor_bottom");
+ } else {
+ _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
+ }
_size_changed();
}
-void Control::set_size(const Size2 &p_size) {
+void Control::_set_size(const Size2 &p_size) {
+ set_size(p_size);
+}
+
+void Control::set_size(const Size2 &p_size, bool p_keep_margins) {
Size2 new_size = p_size;
Size2 min = get_combined_minimum_size();
@@ -1758,7 +1794,15 @@ void Control::set_size(const Size2 &p_size) {
if (new_size.y < min.y)
new_size.y = min.y;
- _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
+ if (p_keep_margins) {
+ _compute_anchors(Rect2(data.pos_cache, new_size), data.margin, data.anchor);
+ _change_notify("anchor_left");
+ _change_notify("anchor_right");
+ _change_notify("anchor_top");
+ _change_notify("anchor_bottom");
+ } else {
+ _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
+ }
_size_changed();
}
@@ -1796,53 +1840,72 @@ Rect2 Control::get_anchorable_rect() const {
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
- ERR_FAIL_COND(p_icon.is_null());
if (data.icon_override.has(p_name)) {
data.icon_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.icon_override[p_name] = p_icon;
- if (data.icon_override[p_name].is_valid()) {
- data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+
+ // clear if "null" is passed instead of a icon
+ if (p_icon.is_null()) {
+ data.icon_override.erase(p_name);
+ } else {
+ data.icon_override[p_name] = p_icon;
+ if (data.icon_override[p_name].is_valid()) {
+ data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
}
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_shader_override(const StringName &p_name, const Ref<Shader> &p_shader) {
- ERR_FAIL_COND(p_shader.is_null());
+
if (data.shader_override.has(p_name)) {
data.shader_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.shader_override[p_name] = p_shader;
- if (data.shader_override[p_name].is_valid()) {
- data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+
+ // clear if "null" is passed instead of a shader
+ if (p_shader.is_null()) {
+ data.shader_override.erase(p_name);
+ } else {
+ data.shader_override[p_name] = p_shader;
+ if (data.shader_override[p_name].is_valid()) {
+ data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
}
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
- ERR_FAIL_COND(p_style.is_null());
if (data.style_override.has(p_name)) {
data.style_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.style_override[p_name] = p_style;
- if (data.style_override[p_name].is_valid()) {
- data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
+ // clear if "null" is passed instead of a style
+ if (p_style.is_null()) {
+ data.style_override.erase(p_name);
+ } else {
+ data.style_override[p_name] = p_style;
+ if (data.style_override[p_name].is_valid()) {
+ data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_font_override(const StringName &p_name, const Ref<Font> &p_font) {
- ERR_FAIL_COND(p_font.is_null());
if (data.font_override.has(p_name)) {
data.font_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.font_override[p_name] = p_font;
- if (data.font_override[p_name].is_valid()) {
- data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
+ // clear if "null" is passed instead of a font
+ if (p_font.is_null()) {
+ data.font_override.erase(p_name);
+ } else {
+ data.font_override[p_name] = p_font;
+ if (data.font_override[p_name].is_valid()) {
+ data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_color_override(const StringName &p_name, const Color &p_color) {
@@ -2631,6 +2694,12 @@ bool Control::is_visibility_clip_disabled() const {
void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+#ifdef TOOLS_ENABLED
+ const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+#else
+ const String quote_style = "\"";
+#endif
+
Node::get_argument_options(p_function, p_idx, r_options);
if (p_idx == 0) {
@@ -2648,7 +2717,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
sn.sort_custom<StringName::AlphCompare>();
for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
- r_options->push_back("\"" + E->get() + "\"");
+ r_options->push_back(quote_style + E->get() + quote_style);
}
}
}
@@ -2693,20 +2762,23 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size);
ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size);
- ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margin"), &Control::set_anchors_preset, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margins"), &Control::set_anchors_preset, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_margins_preset", "preset", "resize_mode", "margin"), &Control::set_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_anchors_and_margins_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("_set_anchor", "margin", "anchor"), &Control::_set_anchor);
+ ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_anchor", "margin"), &Control::get_anchor);
ClassDB::bind_method(D_METHOD("set_margin", "margin", "offset"), &Control::set_margin);
ClassDB::bind_method(D_METHOD("set_anchor_and_margin", "margin", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_margin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin);
ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end);
- ClassDB::bind_method(D_METHOD("set_position", "position"), &Control::set_position);
- ClassDB::bind_method(D_METHOD("set_size", "size"), &Control::set_size);
+ ClassDB::bind_method(D_METHOD("set_position", "position", "keep_margins"), &Control::set_position, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_set_position", "margin"), &Control::_set_position);
+ ClassDB::bind_method(D_METHOD("set_size", "size", "keep_margins"), &Control::set_size, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_set_size", "size"), &Control::_set_size);
ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size);
- ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Control::set_global_position);
+ ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_margins"), &Control::set_global_position, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position);
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation);
ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
@@ -2826,10 +2898,10 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input"));
ADD_GROUP("Anchor", "anchor_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
ADD_GROUP("Margin", "margin_");
ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT);
@@ -2842,9 +2914,9 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Rect", "rect_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "_set_global_position", "get_global_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 5e33f6ba43..a6f9a442ae 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -217,6 +217,9 @@ private:
Control *_get_focus_neighbour(Margin p_margin, int p_count = 0);
void _set_anchor(Margin p_margin, float p_anchor);
+ void _set_position(const Point2 &p_point);
+ void _set_global_position(const Point2 &p_point);
+ void _set_size(const Size2 &p_size);
void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
void _theme_changed();
@@ -229,6 +232,7 @@ private:
Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
+ void _compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]);
void _size_changed();
String _get_tooltip() const;
@@ -325,7 +329,7 @@ public:
/* POSITIONING */
- void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin = true);
+ void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins = true);
void set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
void set_anchors_and_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
@@ -343,12 +347,12 @@ public:
Point2 get_begin() const;
Point2 get_end() const;
- void set_position(const Point2 &p_point);
- void set_global_position(const Point2 &p_point);
+ void set_position(const Point2 &p_point, bool p_keep_margins = false);
+ void set_global_position(const Point2 &p_point, bool p_keep_margins = false);
Point2 get_position() const;
Point2 get_global_position() const;
- void set_size(const Size2 &p_size);
+ void set_size(const Size2 &p_size, bool p_keep_margins = false);
Size2 get_size() const;
Rect2 get_rect() const;
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 059e59ea21..bdb1342019 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -86,7 +86,9 @@ void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
_dir_entered("..");
} break;
- default: { handled = false; }
+ default: {
+ handled = false;
+ }
}
if (handled)
@@ -429,8 +431,6 @@ void FileDialog::update_file_list() {
dirs.pop_front();
}
- dirs.clear();
-
List<String> patterns;
// build filter
if (filter->get_selected() == filter->get_item_count() - 1) {
@@ -505,8 +505,6 @@ void FileDialog::update_file_list() {
if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == NULL)
tree->get_root()->get_children()->select(0);
-
- files.clear();
}
void FileDialog::_filter_selected(int) {
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 68e734502b..dabff08fea 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -770,7 +770,9 @@ void GraphEdit::_top_layer_draw() {
}
if (box_selecting)
- top_layer->draw_rect(box_selecting_rect, Color(0.7, 0.7, 1.0, 0.3));
+ top_layer->draw_rect(
+ box_selecting_rect,
+ get_color("accent_color", "Editor") * Color(1, 1, 1, 0.375));
}
void GraphEdit::set_selected(Node *p_child) {
@@ -1051,7 +1053,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
- if (ABS(E->get().activity - p_activity) < CMP_EPSILON) {
+ if (Math::is_equal_approx(E->get().activity, p_activity)) {
//update only if changed
top_layer->update();
connections_layer->update();
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 8c588109d6..e8692d56d2 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -164,7 +164,6 @@ void GraphNode::_resort() {
vofs += size.y;
}
- _change_notify();
update();
connpos_dirty = true;
}
@@ -409,9 +408,12 @@ Size2 GraphNode::get_minimum_size() const {
void GraphNode::set_title(const String &p_title) {
+ if (title == p_title)
+ return;
title = p_title;
- minimum_size_changed();
update();
+ _change_notify("title");
+ minimum_size_changed();
}
String GraphNode::get_title() const {
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 026374ded1..91b76839d7 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -470,6 +470,8 @@ Size2 ItemList::Item::get_icon_size() const {
void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
+ double prev_scroll = scroll_bar->get_value();
+
Ref<InputEventMouseMotion> mm = p_event;
if (defer_select_single >= 0 && mm.is_valid()) {
defer_select_single = -1;
@@ -747,9 +749,21 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
search_string = "";
}
- search_string += String::chr(k->get_unicode());
- for (int i = 0; i < items.size(); i++) {
- if (items[i].text.begins_with(search_string)) {
+ if (String::chr(k->get_unicode()) != search_string)
+ search_string += String::chr(k->get_unicode());
+
+ for (int i = current + 1; i <= items.size(); i++) {
+ if (i == items.size()) {
+ if (current == 0)
+ break;
+ else
+ i = 0;
+ }
+
+ if (i == current)
+ break;
+
+ if (items[i].text.findn(search_string) == 0) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -767,6 +781,9 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * pan_gesture->get_delta().y / 8);
}
+
+ if (scroll_bar->get_value() != prev_scroll)
+ accept_event(); //accept event if scroll changed
}
void ItemList::ensure_current_is_visible() {
@@ -1243,7 +1260,7 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
Rect2 rc = items[i].rect_cache;
if (i % current_columns == current_columns - 1) {
- rc.size.width = get_size().width; //not right but works
+ rc.size.width = get_size().width - rc.position.x; //make sure you can still select the last item when clicking past the column
}
if (rc.has_point(pos)) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index e84697b80f..58bdde1ffd 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "line_edit.h"
+
#include "core/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -55,6 +56,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
menu->set_position(get_global_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
+ menu->set_scale(get_global_transform().get_scale());
menu->popup();
grab_focus();
return;
@@ -66,6 +68,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
_reset_caret_blink_timer();
if (b->is_pressed()) {
+ accept_event(); //don't pass event further when clicked on text field
if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) {
clear_button_status.press_attempt = true;
clear_button_status.pressing_inside = true;
@@ -157,6 +160,38 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (!k->is_pressed())
return;
+
+#ifdef APPLE_STYLE_KEYS
+ if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) {
+ uint32_t remap_key = KEY_UNKNOWN;
+ switch (k->get_scancode()) {
+ case KEY_F: {
+ remap_key = KEY_RIGHT;
+ } break;
+ case KEY_B: {
+ remap_key = KEY_LEFT;
+ } break;
+ case KEY_P: {
+ remap_key = KEY_UP;
+ } break;
+ case KEY_N: {
+ remap_key = KEY_DOWN;
+ } break;
+ case KEY_D: {
+ remap_key = KEY_DELETE;
+ } break;
+ case KEY_H: {
+ remap_key = KEY_BACKSPACE;
+ } break;
+ }
+
+ if (remap_key != KEY_UNKNOWN) {
+ k->set_scancode(remap_key);
+ k->set_control(false);
+ }
+ }
+#endif
+
unsigned int code = k->get_scancode();
if (k->get_command()) {
@@ -320,7 +355,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
handled = false;
break;
}
- // numlock disabled. fallthrough to key_left
+ FALLTHROUGH;
}
case KEY_LEFT: {
@@ -367,7 +402,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
handled = false;
break;
}
- // numlock disabled. fallthrough to key_right
+ FALLTHROUGH;
}
case KEY_RIGHT: {
@@ -474,7 +509,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
handled = false;
break;
}
- // numlock disabled. fallthrough to key_home
+ FALLTHROUGH;
}
case KEY_HOME: {
@@ -487,7 +522,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
handled = false;
break;
}
- // numlock disabled. fallthrough to key_end
+ FALLTHROUGH;
}
case KEY_END: {
@@ -611,9 +646,7 @@ void LineEdit::_notification(int p_what) {
#endif
case NOTIFICATION_RESIZED: {
- if (expand_to_text_length) {
- window_pos = 0; //force scroll back since it's expanding to text length
- }
+ window_pos = 0;
set_cursor_position(get_cursor_position());
} break;
@@ -721,6 +754,8 @@ void LineEdit::_notification(int p_what) {
} else {
x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
}
+
+ ofs_max -= r_icon->get_width();
}
int caret_height = font->get_height() > y_area ? y_area : font->get_height();
@@ -888,7 +923,8 @@ void LineEdit::cut_text() {
void LineEdit::paste_text() {
- String paste_buffer = OS::get_singleton()->get_clipboard();
+ // Strip escape characters like \n and \t as they can't be displayed on LineEdit.
+ String paste_buffer = OS::get_singleton()->get_clipboard().strip_escapes();
if (paste_buffer != "") {
@@ -917,6 +953,10 @@ void LineEdit::undo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
set_cursor_position(op.cursor_pos);
+
+ if (expand_to_text_length)
+ minimum_size_changed();
+
_emit_text_change();
}
@@ -931,6 +971,10 @@ void LineEdit::redo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
set_cursor_position(op.cursor_pos);
+
+ if (expand_to_text_length)
+ minimum_size_changed();
+
_emit_text_change();
}
@@ -1158,8 +1202,10 @@ void LineEdit::set_cursor_position(int p_pos) {
} else if (cursor_pos > window_pos) {
/* Adjust window if cursor goes too much to the right */
int window_width = get_size().width - style->get_minimum_size().width;
- if (right_icon.is_valid()) {
- window_width -= right_icon->get_width();
+ bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled;
+ if (right_icon.is_valid() || display_clear_icon) {
+ Ref<Texture> r_icon = display_clear_icon ? Control::get_icon("clear") : right_icon;
+ window_width -= r_icon->get_width();
}
if (window_width < 0)
@@ -1223,6 +1269,7 @@ void LineEdit::append_at_cursor(String p_text) {
void LineEdit::clear_internal() {
+ deselect();
_clear_undo_stack();
cached_width = 0;
cursor_pos = 0;
@@ -1313,7 +1360,27 @@ void LineEdit::select_all() {
void LineEdit::set_editable(bool p_editable) {
+ if (editable == p_editable)
+ return;
+
editable = p_editable;
+
+ // Reorganize context menu.
+ menu->clear();
+ if (editable)
+ menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
+ menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
+ if (editable)
+ menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
+ menu->add_separator();
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
+ if (editable) {
+ menu->add_item(RTR("Clear"), MENU_CLEAR);
+ menu->add_separator();
+ menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
+ menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
+ }
+
update();
}
@@ -1609,7 +1676,6 @@ LineEdit::LineEdit() {
deselect();
set_focus_mode(FOCUS_ALL);
- editable = true;
set_default_cursor_shape(CURSOR_IBEAM);
set_mouse_filter(MOUSE_FILTER_STOP);
@@ -1624,15 +1690,8 @@ LineEdit::LineEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
- menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
- menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
- menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
- menu->add_separator();
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
- menu->add_item(RTR("Clear"), MENU_CLEAR);
- menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
- menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
+ editable = false; // initialise to opposite first, so we get past the early-out in set_editable
+ set_editable(true);
menu->connect("id_pressed", this, "menu_option");
expand_to_text_length = false;
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index b67d8c00d6..e12cd55e6f 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -55,8 +55,9 @@ void MenuButton::pressed() {
Size2 size = get_size();
Point2 gp = get_global_position();
- popup->set_global_position(gp + Size2(0, size.height));
+ popup->set_global_position(gp + Size2(0, size.height * get_global_transform().get_scale().y));
popup->set_size(Size2(size.width, 0));
+ popup->set_scale(get_global_transform().get_scale());
popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size()));
popup->popup();
}
@@ -91,6 +92,16 @@ bool MenuButton::is_switch_on_hover() {
return switch_on_hover;
}
+void MenuButton::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+
+ if (!is_visible_in_tree()) {
+ popup->hide();
+ }
+ }
+}
+
void MenuButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup);
@@ -116,15 +127,18 @@ MenuButton::MenuButton() {
switch_on_hover = false;
set_flat(true);
+ set_toggle_mode(true);
set_disable_shortcuts(false);
set_enabled_focus_mode(FOCUS_NONE);
+ set_process_unhandled_key_input(true);
+ set_action_mode(ACTION_MODE_BUTTON_PRESS);
+
popup = memnew(PopupMenu);
popup->hide();
add_child(popup);
- popup->set_as_toplevel(true);
popup->set_pass_on_modal_close_click(false);
- set_process_unhandled_key_input(true);
- set_action_mode(ACTION_MODE_BUTTON_PRESS);
+ popup->connect("about_to_show", this, "set_pressed", varray(true)); // For when switching from another MenuButton.
+ popup->connect("popup_hide", this, "set_pressed", varray(false));
}
MenuButton::~MenuButton() {
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 794840035e..42e909d991 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -52,6 +52,7 @@ class MenuButton : public Button {
void _gui_input(Ref<InputEvent> p_event);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index b9b270ce0c..7027fceb84 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -72,6 +72,11 @@ void OptionButton::_notification(int p_what) {
Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2)));
arrow->draw(ci, ofs, clr);
+ } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+
+ if (!is_visible_in_tree()) {
+ popup->hide();
+ }
}
}
@@ -104,21 +109,21 @@ void OptionButton::_selected(int p_which) {
void OptionButton::pressed() {
Size2 size = get_size();
- popup->set_global_position(get_global_position() + Size2(0, size.height));
+ popup->set_global_position(get_global_position() + Size2(0, size.height * get_global_transform().get_scale().y));
popup->set_size(Size2(size.width, 0));
-
+ popup->set_scale(get_global_transform().get_scale());
popup->popup();
}
-void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
+void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id) {
- popup->add_icon_radio_check_item(p_icon, p_label, p_ID);
+ popup->add_icon_radio_check_item(p_icon, p_label, p_id);
if (popup->get_item_count() == 1)
select(0);
}
-void OptionButton::add_item(const String &p_label, int p_ID) {
+void OptionButton::add_item(const String &p_label, int p_id) {
- popup->add_radio_check_item(p_label, p_ID);
+ popup->add_radio_check_item(p_label, p_id);
if (popup->get_item_count() == 1)
select(0);
}
@@ -131,9 +136,9 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
popup->set_item_icon(p_idx, p_icon);
}
-void OptionButton::set_item_id(int p_idx, int p_ID) {
+void OptionButton::set_item_id(int p_idx, int p_id) {
- popup->set_item_id(p_idx, p_ID);
+ popup->set_item_id(p_idx, p_id);
}
void OptionButton::set_item_metadata(int p_idx, const Variant &p_metadata) {
@@ -333,23 +338,26 @@ void OptionButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
// "selected" property must come after "items", otherwise GH-10213 occurs
ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected");
- ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID")));
- ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "ID")));
+ ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "id")));
}
OptionButton::OptionButton() {
current = -1;
+ set_toggle_mode(true);
set_text_align(ALIGN_LEFT);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
popup = memnew(PopupMenu);
popup->hide();
add_child(popup);
- popup->set_as_toplevel(true);
popup->set_pass_on_modal_close_click(false);
+ popup->set_notify_transform(true);
+ popup->set_allow_search(true);
popup->connect("id_pressed", this, "_selected");
popup->connect("id_focused", this, "_focused");
+ popup->connect("popup_hide", this, "set_pressed", varray(false));
}
OptionButton::~OptionButton() {
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 63b451377a..51d5fd6947 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -59,12 +59,12 @@ protected:
static void _bind_methods();
public:
- void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1);
- void add_item(const String &p_label, int p_ID = -1);
+ void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1);
+ void add_item(const String &p_label, int p_id = -1);
void set_item_text(int p_idx, const String &p_text);
void set_item_icon(int p_idx, const Ref<Texture> &p_icon);
- void set_item_id(int p_idx, int p_ID);
+ void set_item_id(int p_idx, int p_id);
void set_item_metadata(int p_idx, const Variant &p_metadata);
void set_item_disabled(int p_idx, bool p_disabled);
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 80ec7049fc..fdb1b65f77 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -65,7 +65,7 @@ void Popup::_notification(int p_what) {
void Popup::_fix_size() {
Point2 pos = get_global_position();
- Size2 size = get_size();
+ Size2 size = get_size() * get_scale();
Point2 window_size = get_viewport_rect().size;
if (pos.x + size.width > window_size.width)
@@ -115,6 +115,18 @@ void Popup::set_as_minsize() {
set_size(total_minsize);
}
+void Popup::popup_centered_clamped(const Size2 &p_size, float p_fallback_ratio) {
+
+ Size2 popup_size = p_size;
+ Size2 window_size = get_viewport_rect().size;
+
+ // clamp popup size in each dimension if window size is too small (using fallback ratio)
+ popup_size.x = MIN(window_size.x * p_fallback_ratio, popup_size.x);
+ popup_size.y = MIN(window_size.y * p_fallback_ratio, popup_size.y);
+
+ popup_centered(popup_size);
+}
+
void Popup::popup_centered_minsize(const Size2 &p_minsize) {
set_custom_minimum_size(p_minsize);
@@ -179,6 +191,7 @@ void Popup::_bind_methods() {
ClassDB::bind_method(D_METHOD("popup_centered", "size"), &Popup::popup_centered, DEFVAL(Size2()));
ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Popup::popup_centered_ratio, DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("popup_centered_minsize", "minsize"), &Popup::popup_centered_minsize, DEFVAL(Size2()));
+ ClassDB::bind_method(D_METHOD("popup_centered_clamped", "size", "fallback_ratio"), &Popup::popup_centered_clamped, DEFVAL(Size2()), DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("popup", "bounds"), &Popup::popup, DEFVAL(Rect2()));
ClassDB::bind_method(D_METHOD("set_exclusive", "enable"), &Popup::set_exclusive);
ClassDB::bind_method(D_METHOD("is_exclusive"), &Popup::is_exclusive);
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 7ccefe1d75..6615c51ec5 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -64,6 +64,7 @@ public:
void popup_centered(const Size2 &p_size = Size2());
void popup_centered_minsize(const Size2 &p_minsize = Size2());
void set_as_minsize();
+ void popup_centered_clamped(const Size2 &p_size = Size2(), float p_fallback_ratio = 0.75);
virtual void popup(const Rect2 &p_bounds = Rect2());
virtual String get_configuration_warning() const;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 28b124d143..2cac345dae 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -31,6 +31,7 @@
#include "popup_menu.h"
#include "core/os/input.h"
#include "core/os/keyboard.h"
+#include "core/os/os.h"
#include "core/print_string.h"
#include "core/translation.h"
@@ -54,9 +55,11 @@ Size2 PopupMenu::get_minimum_size() const {
Ref<Font> font = get_font("font");
float max_w = 0;
+ float icon_w = 0;
int font_h = font->get_height();
- int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width());
+ int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width()) + hseparation;
int accel_max_w = 0;
+ bool has_check = false;
for (int i = 0; i < items.size(); i++) {
@@ -65,8 +68,7 @@ Size2 PopupMenu::get_minimum_size() const {
Size2 icon_size = items[i].icon->get_size();
size.height = MAX(icon_size.height, font_h);
- size.width += icon_size.width;
- size.width += hseparation;
+ icon_w = MAX(icon_size.width + hseparation, icon_w);
} else {
size.height = font_h;
@@ -74,10 +76,8 @@ Size2 PopupMenu::get_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable_type) {
-
- size.width += check_w + hseparation;
- }
+ if (items[i].checkable_type)
+ has_check = true;
String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
size.width += font->get_string_size(text).width;
@@ -91,16 +91,17 @@ Size2 PopupMenu::get_minimum_size() const {
accel_max_w = MAX(accel_w, accel_max_w);
}
- if (items[i].submenu != "") {
-
+ if (items[i].submenu != "")
size.width += get_icon("submenu")->get_width();
- }
- minsize.height += size.height;
max_w = MAX(max_w, size.width);
+
+ minsize.height += size.height;
}
- minsize.width += max_w + accel_max_w;
+ minsize.width += max_w + icon_w + accel_max_w;
+ if (has_check)
+ minsize.width += check_w;
return minsize;
}
@@ -147,10 +148,10 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
void PopupMenu::_activate_submenu(int over) {
Node *n = get_node(items[over].submenu);
- ERR_EXPLAIN("item subnode does not exist: " + items[over].submenu);
+ ERR_EXPLAIN("Item subnode does not exist: " + items[over].submenu);
ERR_FAIL_COND(!n);
Popup *pm = Object::cast_to<Popup>(n);
- ERR_EXPLAIN("item subnode is not a Popup: " + items[over].submenu);
+ ERR_EXPLAIN("Item subnode is not a Popup: " + items[over].submenu);
ERR_FAIL_COND(!pm);
if (pm->is_visible_in_tree())
return; //already visible!
@@ -159,13 +160,14 @@ void PopupMenu::_activate_submenu(int over) {
Rect2 pr(p, get_size());
Ref<StyleBox> style = get_stylebox("panel");
- Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y);
+ Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y) * get_global_transform().get_scale();
Size2 size = pm->get_size();
// fix pos
if (pos.x + size.width > get_viewport_rect().size.width)
pos.x = p.x - size.width;
pm->set_position(pos);
+ pm->set_scale(get_global_transform().get_scale());
pm->popup();
PopupMenu *pum = Object::cast_to<PopupMenu>(pm);
@@ -196,11 +198,11 @@ void PopupMenu::_scroll(float p_factor, const Point2 &p_over) {
int vseparation = get_constant("vseparation");
Ref<Font> font = get_font("font");
- float dy = (vseparation + font->get_height()) * 3 * p_factor;
+ float dy = (vseparation + font->get_height()) * 3 * p_factor * get_global_transform().get_scale().y;
if (dy > 0 && global_y < 0)
dy = MIN(dy, -global_y - 1);
- else if (dy < 0 && global_y + get_size().y > get_viewport_rect().size.y)
- dy = -MIN(-dy, global_y + get_size().y - get_viewport_rect().size.y - 1);
+ else if (dy < 0 && global_y + get_size().y * get_global_transform().get_scale().y > get_viewport_rect().size.y)
+ dy = -MIN(-dy, global_y + get_size().y * get_global_transform().get_scale().y - get_viewport_rect().size.y - 1);
set_position(get_position() + Vector2(0, dy));
Ref<InputEventMouseMotion> ie;
@@ -239,7 +241,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
for (int i = search_from; i >= 0; i--) {
- if (i < 0 || i >= items.size())
+ if (i >= items.size())
continue;
if (!items[i].separator && !items[i].disabled) {
@@ -289,7 +291,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
case BUTTON_WHEEL_DOWN: {
- if (get_global_position().y + get_size().y > get_viewport_rect().size.y) {
+ if (get_global_position().y + get_size().y * get_global_transform().get_scale().y > get_viewport_rect().size.y) {
_scroll(-b->get_factor(), b->get_position());
}
} break;
@@ -354,7 +356,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
int over = _get_mouse_over(m->get_position());
- int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].ID >= 0 ? items[over].ID : over);
+ int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].id >= 0 ? items[over].id : over);
if (id < 0) {
mouse_over = -1;
@@ -379,6 +381,43 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll(-pan_gesture->get_delta().y, pan_gesture->get_position());
}
}
+
+ Ref<InputEventKey> k = p_event;
+
+ if (allow_search && k.is_valid() && k->get_unicode()) {
+
+ uint64_t now = OS::get_singleton()->get_ticks_msec();
+ uint64_t diff = now - search_time_msec;
+ uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
+ search_time_msec = now;
+
+ if (diff > max_interval) {
+ search_string = "";
+ }
+
+ if (String::chr(k->get_unicode()) != search_string)
+ search_string += String::chr(k->get_unicode());
+
+ for (int i = mouse_over + 1; i <= items.size(); i++) {
+ if (i == items.size()) {
+ if (mouse_over <= 0)
+ break;
+ else
+ i = 0;
+ }
+
+ if (i == mouse_over)
+ break;
+
+ if (items[i].text.findn(search_string) == 0) {
+ mouse_over = i;
+ emit_signal("id_focused", i);
+ update();
+ accept_event();
+ break;
+ }
+ }
+ }
}
bool PopupMenu::has_point(const Point2 &p_point) const {
@@ -415,7 +454,6 @@ void PopupMenu::_notification(int p_what) {
minimum_size_changed();
update();
-
} break;
case NOTIFICATION_DRAW: {
@@ -443,15 +481,31 @@ void PopupMenu::_notification(int p_what) {
Color font_color_hover = get_color("font_color_hover");
float font_h = font->get_height();
+ // Add the check and the wider icon to the offset of all items.
+ float icon_ofs = 0.0;
+ bool has_check = false;
+ for (int i = 0; i < items.size(); i++) {
+
+ if (!items[i].icon.is_null())
+ icon_ofs = MAX(items[i].icon->get_size().width, icon_ofs);
+
+ if (items[i].checkable_type)
+ has_check = true;
+ }
+ if (icon_ofs > 0.0)
+ icon_ofs += hseparation;
+
+ float check_ofs = 0.0;
+ if (has_check)
+ check_ofs = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width()) + hseparation;
+
for (int i = 0; i < items.size(); i++) {
if (i > 0)
ofs.y += vseparation;
Point2 item_ofs = ofs;
- float h;
Size2 icon_size;
-
- Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ float h;
if (!items[i].icon.is_null()) {
@@ -489,16 +543,15 @@ void PopupMenu::_notification(int p_what) {
}
}
+ Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+
if (items[i].checkable_type) {
Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
- item_ofs.x += icon->get_width() + hseparation;
}
if (!items[i].icon.is_null()) {
- items[i].icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
- item_ofs.x += items[i].icon->get_width();
- item_ofs.x += hseparation;
+ items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
}
if (items[i].submenu != "") {
@@ -514,6 +567,7 @@ void PopupMenu::_notification(int p_what) {
}
} else {
+ item_ofs.x += icon_ofs + check_ofs;
font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
}
@@ -528,7 +582,6 @@ void PopupMenu::_notification(int p_what) {
ofs.y += h;
}
-
} break;
case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
@@ -576,93 +629,93 @@ void PopupMenu::_notification(int p_what) {
}
}
-void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.icon = p_icon;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id == -1 ? items.size() : p_id;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_ID) {
+void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
- item.ID = p_ID;
+ item.id = p_id;
item.submenu = p_submenu;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.icon = p_icon;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id == -1 ? items.size() : p_id;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p_accel) {
- add_check_item(p_label, p_ID, p_accel);
+ add_check_item(p_label, p_id, p_accel);
items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
-void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
- add_icon_check_item(p_icon, p_label, p_ID, p_accel);
+ add_icon_check_item(p_icon, p_label, p_id, p_accel);
items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
-void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.icon = p_icon;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
@@ -671,14 +724,14 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut
minimum_size_changed();
}
-void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
items.push_back(item);
@@ -686,14 +739,14 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g
minimum_size_changed();
}
-void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.shortcut = p_shortcut;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
item.icon = p_icon;
@@ -703,14 +756,14 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh
minimum_size_changed();
}
-void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
@@ -719,21 +772,21 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
minimum_size_changed();
}
-void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
- add_check_shortcut(p_shortcut, p_ID, p_global);
+ add_check_shortcut(p_shortcut, p_id, p_global);
items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
-void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id;
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
@@ -767,10 +820,10 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
update();
minimum_size_changed();
}
-void PopupMenu::set_item_id(int p_idx, int p_ID) {
+void PopupMenu::set_item_id(int p_idx, int p_id) {
ERR_FAIL_INDEX(p_idx, items.size());
- items.write[p_idx].ID = p_ID;
+ items.write[p_idx].id = p_id;
update();
minimum_size_changed();
@@ -866,14 +919,14 @@ bool PopupMenu::is_item_checked(int p_idx) const {
int PopupMenu::get_item_id(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), 0);
- return items[p_idx].ID;
+ return items[p_idx].id;
}
-int PopupMenu::get_item_index(int p_ID) const {
+int PopupMenu::get_item_index(int p_id) const {
for (int i = 0; i < items.size(); i++) {
- if (items[i].ID == p_ID)
+ if (items[i].id == p_id)
return i;
}
@@ -1062,7 +1115,7 @@ void PopupMenu::activate_item(int p_item) {
ERR_FAIL_INDEX(p_item, items.size());
ERR_FAIL_COND(items[p_item].separator);
- int id = items[p_item].ID >= 0 ? items[p_item].ID : p_item;
+ int id = items[p_item].id >= 0 ? items[p_item].id : p_item;
//hide all parent PopupMenus
Node *next = get_parent();
@@ -1124,7 +1177,7 @@ void PopupMenu::add_separator(const String &p_text) {
Item sep;
sep.separator = true;
- sep.ID = -1;
+ sep.id = -1;
if (p_text != String()) {
sep.text = p_text;
sep.xl_text = tr(p_text);
@@ -1274,6 +1327,16 @@ float PopupMenu::get_submenu_popup_delay() const {
return submenu_timer->get_wait_time();
}
+void PopupMenu::set_allow_search(bool p_allow) {
+
+ allow_search = p_allow;
+}
+
+bool PopupMenu::get_allow_search() const {
+
+ return allow_search;
+}
+
void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) {
hide_on_window_lose_focus = p_enabled;
@@ -1392,6 +1455,9 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus);
ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus);
+ ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
+ ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
+
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
@@ -1399,9 +1465,10 @@ void PopupMenu::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
- ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
- ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID")));
+ ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
}
@@ -1420,6 +1487,10 @@ PopupMenu::PopupMenu() {
initial_button_mask = 0;
during_grabbed_click = false;
+ allow_search = false;
+ search_time_msec = 0;
+ search_string = "";
+
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
set_hide_on_item_selection(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 767ff568fe..babdd21281 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -55,7 +55,7 @@ class PopupMenu : public Popup {
int state;
bool separator;
bool disabled;
- int ID;
+ int id;
Variant metadata;
String submenu;
String tooltip;
@@ -112,6 +112,10 @@ class PopupMenu : public Popup {
void _ref_shortcut(Ref<ShortCut> p_sc);
void _unref_shortcut(Ref<ShortCut> p_sc);
+ bool allow_search;
+ uint64_t search_time_msec;
+ String search_string;
+
protected:
virtual bool has_point(const Point2 &p_point) const;
@@ -120,26 +124,26 @@ protected:
static void _bind_methods();
public:
- void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
-
- void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
-
- void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0);
+ void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_radio_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1);
+
+ void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+
+ void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id = -1, uint32_t p_accel = 0);
void set_item_text(int p_idx, const String &p_text);
void set_item_icon(int p_idx, const Ref<Texture> &p_icon);
void set_item_checked(int p_idx, bool p_checked);
- void set_item_id(int p_idx, int p_ID);
+ void set_item_id(int p_idx, int p_id);
void set_item_accelerator(int p_idx, uint32_t p_accel);
void set_item_metadata(int p_idx, const Variant &p_meta);
void set_item_disabled(int p_idx, bool p_disabled);
@@ -161,7 +165,7 @@ public:
Ref<Texture> get_item_icon(int p_idx) const;
bool is_item_checked(int p_idx) const;
int get_item_id(int p_idx) const;
- int get_item_index(int p_ID) const;
+ int get_item_index(int p_id) const;
uint32_t get_item_accelerator(int p_idx) const;
Variant get_item_metadata(int p_idx) const;
bool is_item_disabled(int p_idx) const;
@@ -206,6 +210,9 @@ public:
void set_submenu_popup_delay(float p_time);
float get_submenu_popup_delay() const;
+ void set_allow_search(bool p_allow);
+ bool get_allow_search() const;
+
virtual void popup(const Rect2 &p_bounds = Rect2());
void set_hide_on_window_lose_focus(bool p_enabled);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 9bcf10d5e7..da452e3f10 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -711,7 +711,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
} break;
- default: {}
+ default: {
+ }
}
Item *itp = it;
@@ -752,6 +753,8 @@ void RichTextLabel::_scroll_changed(double) {
else
scroll_following = false;
+ scroll_updated = true;
+
update();
}
@@ -777,7 +780,6 @@ void RichTextLabel::_update_scroll() {
main->first_invalid_line = 0; //invalidate ALL
_validate_line_caches(main);
}
- scroll_updated = true;
}
void RichTextLabel::_notification(int p_what) {
@@ -1144,8 +1146,8 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
emit_signal("meta_hover_started", meta);
}
} else if (meta_hovering) {
- emit_signal("meta_hover_ended", current_meta);
meta_hovering = NULL;
+ emit_signal("meta_hover_ended", current_meta);
current_meta = false;
}
}
@@ -2004,7 +2006,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
Item *it = main;
int charidx = 0;
- if (p_from_selection && selection.active && selection.enabled) {
+ if (p_from_selection && selection.active) {
it = selection.to;
charidx = selection.to_char + 1;
}
@@ -2334,6 +2336,7 @@ RichTextLabel::RichTextLabel() {
tab_size = 4;
default_align = ALIGN_LEFT;
underline_meta = true;
+ meta_hovering = NULL;
override_selected_font_color = false;
scroll_visible = false;
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 2938654ed9..686d1c96cc 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -53,29 +53,19 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (b.is_valid()) {
accept_event();
- if (b->get_button_index() == 5 && b->is_pressed()) {
+ if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) {
- /*
- if (orientation==VERTICAL)
- set_val( get_val() + get_page() / 4.0 );
- else
- */
set_value(get_value() + get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() == 4 && b->is_pressed()) {
+ if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
- /*
- if (orientation==HORIZONTAL)
- set_val( get_val() - get_page() / 4.0 );
- else
- */
set_value(get_value() - get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() != 1)
+ if (b->get_button_index() != BUTTON_LEFT)
return;
if (b->is_pressed()) {
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index e50a71b0ff..a1034937b5 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -88,13 +88,16 @@ void ScrollContainer::_cancel_drag() {
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
+ double prev_v_scroll = v_scroll->get_value();
+ double prev_h_scroll = h_scroll->get_value();
+
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
- if (h_scroll->is_visible() && !v_scroll->is_visible()) {
+ if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) {
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8 * mb->get_factor());
} else if (v_scroll->is_visible_in_tree()) {
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 8 * mb->get_factor());
@@ -103,7 +106,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
- if (h_scroll->is_visible() && !v_scroll->is_visible()) {
+ if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8 * mb->get_factor());
} else if (v_scroll->is_visible()) {
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 8 * mb->get_factor());
@@ -122,6 +125,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
+
if (!OS::get_singleton()->has_touchscreen_ui_hint())
return;
@@ -204,6 +210,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8);
}
}
+
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
}
void ScrollContainer::_update_scrollbar_position() {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index eb04b85931..b777e77bc3 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -34,8 +34,15 @@
Size2 Slider::get_minimum_size() const {
Ref<StyleBox> style = get_stylebox("slider");
- Size2i ms = style->get_minimum_size() + style->get_center_size();
- return ms;
+ Size2i ss = style->get_minimum_size() + style->get_center_size();
+
+ Ref<Texture> grabber = get_icon("grabber");
+ Size2i rs = grabber->get_size();
+
+ if (orientation == HORIZONTAL)
+ return Size2i(ss.width, MAX(ss.height, rs.height));
+ else
+ return Size2i(MAX(ss.width, rs.width), ss.height);
}
void Slider::_gui_input(Ref<InputEvent> p_event) {
@@ -134,7 +141,11 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
void Slider::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ minimum_size_changed();
+ update();
+ } break;
case NOTIFICATION_MOUSE_ENTER: {
mouse_inside = true;
@@ -284,6 +295,7 @@ Slider::Slider(Orientation p_orientation) {
mouse_inside = false;
grab.active = false;
ticks = 0;
+ ticks_on_borders = false;
custom_step = -1;
editable = true;
scrollable = true;
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index d21143739c..e778af3ceb 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -277,6 +277,7 @@ SpinBox::SpinBox() {
add_child(line_edit);
line_edit->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
//connect("value_changed",this,"_value_changed");
line_edit->connect("text_entered", this, "_text_entered", Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("focus_exited", this, "_line_edit_focus_exit", Vector<Variant>(), CONNECT_DEFERRED);
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 212efa4976..39c76e6646 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -86,8 +86,8 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
emit_signal("pre_popup_pressed");
Vector2 popup_pos = get_global_position();
- popup_pos.x += size.width - popup->get_size().width;
- popup_pos.y += menu->get_height();
+ popup_pos.x += size.width * get_global_transform().get_scale().x - popup->get_size().width * popup->get_global_transform().get_scale().x;
+ popup_pos.y += menu->get_height() * get_global_transform().get_scale().y;
popup->set_global_position(popup_pos);
popup->popup();
@@ -127,6 +127,9 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
// Activate the clicked tab.
pos.x -= tabs_ofs_cache;
for (int i = first_tab_cache; i <= last_tab_cache; i++) {
+ if (get_tab_hidden(i)) {
+ continue;
+ }
int tab_width = _get_tab_width(i);
if (pos.x < tab_width) {
if (!get_tab_disabled(i)) {
@@ -143,6 +146,11 @@ void TabContainer::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+
+ minimum_size_changed();
+ update();
+ } break;
case NOTIFICATION_RESIZED: {
Vector<Control *> tabs = _get_tabs();
@@ -178,7 +186,6 @@ void TabContainer::_notification(int p_what) {
first_tab_cache--;
}
} break;
-
case NOTIFICATION_DRAW: {
RID canvas = get_canvas_item();
@@ -216,6 +223,9 @@ void TabContainer::_notification(int p_what) {
// Check if all tabs would fit into the header area.
int all_tabs_width = 0;
for (int i = 0; i < tabs.size(); i++) {
+ if (get_tab_hidden(i)) {
+ continue;
+ }
int tab_width = _get_tab_width(i);
all_tabs_width += tab_width;
@@ -241,6 +251,9 @@ void TabContainer::_notification(int p_what) {
all_tabs_width = 0;
Vector<int> tab_widths;
for (int i = first_tab_cache; i < tabs.size(); i++) {
+ if (get_tab_hidden(i)) {
+ continue;
+ }
int tab_width = _get_tab_width(i);
if (all_tabs_width + tab_width > header_width && tab_widths.size() > 0)
break;
@@ -267,6 +280,9 @@ void TabContainer::_notification(int p_what) {
// Draw all visible tabs.
int x = 0;
for (int i = 0; i < tab_widths.size(); i++) {
+ if (get_tab_hidden(i)) {
+ continue;
+ }
Ref<StyleBox> tab_style;
Color font_color;
if (get_tab_disabled(i + first_tab_cache)) {
@@ -338,6 +354,7 @@ void TabContainer::_notification(int p_what) {
}
} break;
case NOTIFICATION_THEME_CHANGED: {
+
minimum_size_changed();
call_deferred("_on_theme_changed"); //wait until all changed theme
} break;
@@ -354,7 +371,7 @@ int TabContainer::_get_tab_width(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
Control *control = Object::cast_to<Control>(_get_tabs()[p_index]);
- if (!control || control->is_set_as_toplevel())
+ if (!control || control->is_set_as_toplevel() || get_tab_hidden(p_index))
return 0;
// Get the width of the text displayed on the tab.
@@ -719,6 +736,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);
+ update();
}
String TabContainer::get_tab_title(int p_tab) const {
@@ -736,6 +754,7 @@ void TabContainer::set_tab_icon(int p_tab, const Ref<Texture> &p_icon) {
Control *child = _get_tab(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_icon", p_icon);
+ update();
}
Ref<Texture> TabContainer::get_tab_icon(int p_tab) const {
@@ -765,6 +784,36 @@ bool TabContainer::get_tab_disabled(int p_tab) const {
return false;
}
+void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
+
+ Control *child = _get_tab(p_tab);
+ ERR_FAIL_COND(!child);
+ child->set_meta("_tab_hidden", p_hidden);
+ update();
+ for (int i = 0; i < get_tab_count(); i++) {
+ int try_tab = (p_tab + 1 + i) % get_tab_count();
+ if (get_tab_disabled(try_tab) || get_tab_hidden(try_tab)) {
+ continue;
+ }
+
+ set_current_tab(try_tab);
+ return;
+ }
+
+ //assumed no other tab can be switched to, just hide
+ child->hide();
+}
+
+bool TabContainer::get_tab_hidden(int p_tab) const {
+
+ Control *child = _get_tab(p_tab);
+ ERR_FAIL_COND_V(!child, false);
+ if (child->has_meta("_tab_hidden"))
+ return child->get_meta("_tab_hidden");
+ else
+ return false;
+}
+
void TabContainer::get_translatable_strings(List<String> *p_strings) const {
Vector<Control *> tabs = _get_tabs();
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index c110f041d0..f7a9fb64fd 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -96,6 +96,9 @@ public:
void set_tab_disabled(int p_tab, bool p_disabled);
bool get_tab_disabled(int p_tab) const;
+ void set_tab_hidden(int p_tab, bool p_hidden);
+ bool get_tab_hidden(int p_tab) const;
+
int get_tab_count() const;
void set_current_tab(int p_current);
int get_current_tab() const;
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index ac643c1320..36571cc878 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -222,6 +222,10 @@ void Tabs::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ minimum_size_changed();
+ update();
+ } break;
case NOTIFICATION_MOUSE_EXIT: {
rb_hover = -1;
cb_hover = -1;
@@ -232,7 +236,6 @@ void Tabs::_notification(int p_what) {
_update_cache();
_ensure_no_over_offset();
ensure_tab_visible(current);
-
} break;
case NOTIFICATION_DRAW: {
_update_cache();
@@ -394,7 +397,6 @@ void Tabs::_notification(int p_what) {
} else {
buttons_visible = false;
}
-
} break;
}
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 92421dff35..6203b15992 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -104,6 +104,13 @@ static CharType _get_right_pair_symbol(CharType c) {
return 0;
}
+static int _find_first_non_whitespace_column_of_line(const String &line) {
+ int left = 0;
+ while (left < line.length() && _is_whitespace(line[left]))
+ left++;
+ return left;
+}
+
void TextEdit::Text::set_font(const Ref<Font> &p_font) {
font = p_font;
@@ -292,6 +299,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
line.marked = false;
line.safe = false;
line.breakpoint = false;
+ line.bookmark = false;
line.hidden = false;
line.width_cache = -1;
line.wrap_amount_cache = -1;
@@ -346,10 +354,14 @@ void TextEdit::_update_scrollbars() {
if (line_numbers)
total_width += cache.line_number_w;
- if (draw_breakpoint_gutter) {
+ if (draw_breakpoint_gutter || draw_bookmark_gutter) {
total_width += cache.breakpoint_gutter_width;
}
+ if (draw_info_gutter) {
+ total_width += cache.info_gutter_width;
+ }
+
if (draw_fold_gutter) {
total_width += cache.fold_gutter_width;
}
@@ -394,6 +406,7 @@ void TextEdit::_update_scrollbars() {
cursor.line_ofs = 0;
cursor.wrap_ofs = 0;
v_scroll->set_value(0);
+ v_scroll->set_max(0);
v_scroll->hide();
}
@@ -412,6 +425,7 @@ void TextEdit::_update_scrollbars() {
cursor.x_ofs = 0;
h_scroll->set_value(0);
+ h_scroll->set_max(0);
h_scroll->hide();
}
@@ -590,18 +604,31 @@ void TextEdit::_notification(int p_what) {
}
} break;
case NOTIFICATION_DRAW: {
+
+ if (first_draw) {
+ //size may not be the final one, so attempts to ensure cursor was visible may have failed
+ adjust_viewport_to_cursor();
+ first_draw = false;
+ }
Size2 size = get_size();
if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
draw_caret = false;
}
- if (draw_breakpoint_gutter) {
+ if (draw_breakpoint_gutter || draw_bookmark_gutter) {
breakpoint_gutter_width = (get_row_height() * 55) / 100;
cache.breakpoint_gutter_width = breakpoint_gutter_width;
} else {
cache.breakpoint_gutter_width = 0;
}
+ if (draw_info_gutter) {
+ info_gutter_width = (get_row_height());
+ cache.info_gutter_width = info_gutter_width;
+ } else {
+ cache.info_gutter_width = 0;
+ }
+
if (draw_fold_gutter) {
fold_gutter_width = (get_row_height() * 55) / 100;
cache.fold_gutter_width = fold_gutter_width;
@@ -631,7 +658,7 @@ void TextEdit::_notification(int p_what) {
RID ci = get_canvas_item();
VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
- int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width;
+ int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT);
//let's do it easy for now:
cache.style_normal->draw(ci, Rect2(Point2(), size));
@@ -808,6 +835,9 @@ 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)
+ bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String();
+
String line_num_padding = line_numbers_zero_padded ? "0" : " ";
int cursor_wrap_index = get_cursor_wrap_index();
@@ -934,6 +964,16 @@ void TextEdit::_notification(int p_what) {
#endif
}
+ // draw bookmark marker
+ if (text.is_bookmark(line)) {
+ if (draw_bookmark_gutter) {
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
+ int marker_radius = get_row_height() - (vertical_gap * 2);
+ VisualServer::get_singleton()->canvas_item_add_circle(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 + marker_radius / 2, ofs_y + vertical_gap + marker_radius / 2), marker_radius, Color(cache.bookmark_color.r, cache.bookmark_color.g, cache.bookmark_color.b));
+ }
+ }
+
// draw breakpoint marker
if (text.is_breakpoint(line)) {
if (draw_breakpoint_gutter) {
@@ -946,10 +986,53 @@ void TextEdit::_notification(int p_what) {
}
}
+ // draw info icons
+ if (draw_info_gutter && text.has_info_icon(line)) {
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.info_gutter_width * 30) / 100;
+ int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width;
+
+ Ref<Texture> info_icon = text.get_info_icon(line);
+ // ensure the icon fits the gutter size
+ Size2i icon_size = info_icon->get_size();
+ if (icon_size.width > cache.info_gutter_width - horizontal_gap) {
+ icon_size.width = cache.info_gutter_width - horizontal_gap;
+ }
+ if (icon_size.height > get_row_height() - horizontal_gap) {
+ icon_size.height = get_row_height() - horizontal_gap;
+ }
+
+ Size2i icon_pos;
+ int xofs = horizontal_gap - (info_icon->get_width() / 4);
+ int yofs = vertical_gap - (info_icon->get_height() / 4);
+ icon_pos.x = gutter_left + xofs + ofs_x;
+ icon_pos.y = ofs_y + yofs;
+
+ draw_texture_rect(info_icon, Rect2(icon_pos, icon_size));
+ }
+
+ // draw execution marker
+ if (executing_line == line) {
+ if (draw_breakpoint_gutter) {
+ int icon_extra_size = 4;
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
+ int marker_height = get_row_height() - (vertical_gap * 2) + icon_extra_size;
+ int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2) + icon_extra_size;
+ cache.executing_icon->draw_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 - icon_extra_size / 2, ofs_y + vertical_gap - icon_extra_size / 2, marker_width, marker_height), false, Color(cache.executing_line_color.r, cache.executing_line_color.g, cache.executing_line_color.b));
+ } else {
+#ifdef TOOLS_ENABLED
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.executing_line_color);
+#else
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.executing_line_color);
+#endif
+ }
+ }
+
// draw fold markers
if (draw_fold_gutter) {
int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
- int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w;
+ int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
if (is_folded(line)) {
int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
@@ -969,7 +1052,7 @@ void TextEdit::_notification(int p_what) {
fc = line_num_padding + fc;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
+ cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.info_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
}
}
@@ -1009,10 +1092,7 @@ void TextEdit::_notification(int p_what) {
}
if ((char_ofs + char_margin + char_w) >= xmargin_end) {
- if (syntax_coloring)
- continue;
- else
- break;
+ break;
}
bool in_search_result = false;
@@ -1035,7 +1115,7 @@ void TextEdit::_notification(int p_what) {
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
// draw the wrap indent offset highlight
if (line_wrap_index != 0 && j == 0) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, indent_px, get_row_height()), cache.current_line_color);
}
// if its the last char draw to end of the line
if (j == str.length() - 1) {
@@ -1063,7 +1143,7 @@ void TextEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color);
}
- if (highlight_all_occurrences) {
+ if (highlight_all_occurrences && !only_whitespaces_highlighted) {
if (highlighted_text_col != -1) {
// if we are at the end check for new word on same line
@@ -1192,6 +1272,11 @@ void TextEdit::_notification(int p_what) {
cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
+ if (draw_spaces && str[j] == ' ') {
+ int yofs = (get_row_height() - cache.space_icon->get_height()) / 2;
+ cache.space_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ }
+
char_ofs += char_w;
if (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) {
@@ -1497,8 +1582,7 @@ void TextEdit::_consume_pair_symbol(CharType ch) {
}
if ((ch == '\'' || ch == '"') &&
- cursor_get_column() > 0 &&
- _is_text_char(text[cursor.line][cursor_get_column() - 1])) {
+ cursor_get_column() > 0 && _is_text_char(text[cursor.line][cursor_get_column() - 1]) && !_is_pair_right_symbol(text[cursor.line][cursor_get_column()])) {
insert_text_at_cursor(ch_single);
cursor_set_column(cursor_position_to_move);
return;
@@ -1560,37 +1644,35 @@ void TextEdit::backspace_at_cursor() {
set_line_as_breakpoint(prev_line, true);
}
+ if (text.has_info_icon(cursor.line)) {
+ set_line_info_icon(prev_line, text.get_info_icon(cursor.line), text.get_info(cursor.line));
+ }
+
if (auto_brace_completion_enabled &&
cursor.column > 0 &&
_is_pair_left_symbol(text[cursor.line][cursor.column - 1])) {
_consume_backspace_for_pair_symbol(prev_line, prev_column);
} else {
// handle space indentation
- if (cursor.column - indent_size >= 0 && indent_using_spaces) {
-
- // if there is enough spaces to count as a tab
+ if (cursor.column != 0 && indent_using_spaces) {
+ // check if there are no other chars before cursor, just indentation
bool unindent = true;
- for (int i = 1; i <= indent_size; i++) {
- if (text[cursor.line][cursor.column - i] != ' ') {
- unindent = false;
- break;
- }
- }
-
- // and it is before the first character
int i = 0;
while (i < cursor.column && i < text[cursor.line].length()) {
- if (text[cursor.line][i] != ' ' && text[cursor.line][i] != '\t') {
+ if (!_is_whitespace(text[cursor.line][i])) {
unindent = false;
break;
}
i++;
}
- // then we can remove it as a single character.
+ // then we can remove all spaces as a single character.
if (unindent) {
- _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column);
- prev_column = cursor.column - indent_size;
+ // we want to remove spaces up to closest indent
+ // or whole indent if cursor is pointing at it
+ int spaces_to_delete = _calculate_spaces_till_next_left_indent(cursor.column);
+ prev_column = cursor.column - spaces_to_delete;
+ _remove_text(cursor.line, prev_column, cursor.line, cursor.column);
} else {
_remove_text(prev_line, prev_column, cursor.line, cursor.column);
}
@@ -1607,6 +1689,10 @@ void TextEdit::indent_right() {
int start_line;
int end_line;
+
+ // this value informs us by how much we changed selection position by indenting right
+ // default is 1 for tab indentation
+ int selection_offset = 1;
begin_complex_operation();
if (is_selection_active()) {
@@ -1625,18 +1711,24 @@ void TextEdit::indent_right() {
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
if (indent_using_spaces) {
- line_text = space_indent + line_text;
+ // we don't really care where selection is - we just need to know indentation level at the beginning of the line
+ int left = _find_first_non_whitespace_column_of_line(line_text);
+ int spaces_to_add = _calculate_spaces_till_next_right_indent(left);
+ // since we will add this much spaces we want move whole selection and cursor by this much
+ selection_offset = spaces_to_add;
+ for (int j = 0; j < spaces_to_add; j++)
+ line_text = ' ' + line_text;
} else {
line_text = '\t' + line_text;
}
set_line(i, line_text);
}
- // fix selection and cursor being off by one on the last line
+ // fix selection and cursor being off after shifting selection right
if (is_selection_active()) {
- select(selection.from_line, selection.from_column + 1, selection.to_line, selection.to_column + 1);
+ select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset);
}
- cursor_set_column(cursor.column + 1, false);
+ cursor_set_column(cursor.column + selection_offset, false);
end_complex_operation();
update();
}
@@ -1645,6 +1737,15 @@ void TextEdit::indent_left() {
int start_line;
int end_line;
+
+ // moving cursor and selection after unindenting can get tricky
+ // because changing content of line can move cursor and selection on it's own (if new line ends before previous position of either)
+ // therefore we just remember initial values
+ // and at the end of the operation offset them by number of removed characters
+ int removed_characters = 0;
+ int initial_selection_end_column = selection.to_column;
+ int initial_cursor_column = cursor.column;
+
begin_complex_operation();
if (is_selection_active()) {
@@ -1667,21 +1768,43 @@ void TextEdit::indent_left() {
if (line_text.begins_with("\t")) {
line_text = line_text.substr(1, line_text.length());
set_line(i, line_text);
- } else if (line_text.begins_with(space_indent)) {
- line_text = line_text.substr(indent_size, line_text.length());
+ removed_characters = 1;
+ } else if (line_text.begins_with(" ")) {
+ // when unindenting we aim to remove spaces before line that has selection no matter what is selected
+ // so we start of by finding first non whitespace character of line
+ int left = _find_first_non_whitespace_column_of_line(line_text);
+
+ // here we remove only enough spaces to align text to nearest full multiple of indentation_size
+ // in case where selection begins at the start of indentation_size multiple we remove whole indentation level
+ int spaces_to_remove = _calculate_spaces_till_next_left_indent(left);
+
+ line_text = line_text.substr(spaces_to_remove, line_text.length());
set_line(i, line_text);
+ removed_characters = spaces_to_remove;
}
}
// fix selection and cursor being off by one on the last line
if (is_selection_active() && last_line_text != get_line(end_line)) {
- select(selection.from_line, selection.from_column - 1, selection.to_line, selection.to_column - 1);
+ select(selection.from_line, selection.from_column - removed_characters,
+ selection.to_line, initial_selection_end_column - removed_characters);
}
- cursor_set_column(cursor.column - 1, false);
+ cursor_set_column(initial_cursor_column - removed_characters, false);
end_complex_operation();
update();
}
+int TextEdit::_calculate_spaces_till_next_left_indent(int column) {
+ int spaces_till_indent = column % indent_size;
+ if (spaces_till_indent == 0)
+ spaces_till_indent = indent_size;
+ return spaces_till_indent;
+}
+
+int TextEdit::_calculate_spaces_till_next_right_indent(int column) {
+ return indent_size - column % indent_size;
+}
+
void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const {
float rows = p_mouse.y;
@@ -1712,7 +1835,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
col = text[row].size();
} else {
- int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width);
+ int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width);
colx += cursor.x_ofs;
col = get_char_pos_for_line(colx, row, wrap_index);
if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
@@ -1733,6 +1856,9 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
+ double prev_v_scroll = v_scroll->get_value();
+ double prev_h_scroll = h_scroll->get_value();
+
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
@@ -1810,18 +1936,28 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// toggle breakpoint on gutter click
if (draw_breakpoint_gutter) {
int gutter = cache.style_normal->get_margin(MARGIN_LEFT);
- if (mb->get_position().x > gutter && mb->get_position().x <= gutter + cache.breakpoint_gutter_width + 3) {
+ if (mb->get_position().x > gutter - 6 && mb->get_position().x <= gutter + cache.breakpoint_gutter_width - 3) {
set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row));
emit_signal("breakpoint_toggled", row);
return;
}
}
+ // emit info clicked
+ if (draw_info_gutter && text.has_info_icon(row)) {
+ int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
+ int gutter_left = left_margin + cache.breakpoint_gutter_width;
+ if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.info_gutter_width - 3) {
+ emit_signal("info_clicked", row, text.get_info(row));
+ return;
+ }
+ }
+
// toggle fold on gutter click if can
if (draw_fold_gutter) {
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w;
+ int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.fold_gutter_width - 3) {
if (is_folded(row)) {
unfold_line(row);
@@ -1835,7 +1971,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// unfold on folded icon click
if (is_folded(row)) {
int line_width = text.get_line_width(row);
- line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
+ line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.info_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
if (mb->get_position().x > line_width - 3 && mb->get_position().x <= line_width + cache.folded_eol_icon->get_width() + 3) {
unfold_line(row);
return;
@@ -1953,6 +2089,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
menu->set_position(get_global_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
+ menu->set_scale(get_global_transform().get_scale());
menu->popup();
grab_focus();
}
@@ -1976,6 +2113,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_scroll_down(delta);
}
h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100);
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
+
return;
}
@@ -2019,6 +2159,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
+
Ref<InputEventKey> k = p_gui_input;
if (k.is_valid()) {
@@ -2198,6 +2341,36 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
k->set_command(true);
k->set_shift(false);
}
+#ifdef APPLE_STYLE_KEYS
+ if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) {
+ uint32_t remap_key = KEY_UNKNOWN;
+ switch (k->get_scancode()) {
+ case KEY_F: {
+ remap_key = KEY_RIGHT;
+ } break;
+ case KEY_B: {
+ remap_key = KEY_LEFT;
+ } break;
+ case KEY_P: {
+ remap_key = KEY_UP;
+ } break;
+ case KEY_N: {
+ remap_key = KEY_DOWN;
+ } break;
+ case KEY_D: {
+ remap_key = KEY_DELETE;
+ } break;
+ case KEY_H: {
+ remap_key = KEY_BACKSPACE;
+ } break;
+ }
+
+ if (remap_key != KEY_UNKNOWN) {
+ k->set_scancode(remap_key);
+ k->set_control(false);
+ }
+ }
+#endif
_reset_caret_blink_timer();
@@ -2397,15 +2570,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
if (k->get_shift()) {
- //simple unindent
+ // simple unindent
int cc = cursor.column;
-
- const int len = text[cursor.line].length();
const String &line = text[cursor.line];
- int left = 0; // number of whitespace chars at beginning of line
- while (left < len && (line[left] == '\t' || line[left] == ' '))
- left++;
+ int left = _find_first_non_whitespace_column_of_line(line);
cc = MIN(cc, left);
while (cc < indent_size && cc < left && line[cc] == ' ')
@@ -2413,24 +2582,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (cc > 0 && cc <= text[cursor.line].length()) {
if (text[cursor.line][cc - 1] == '\t') {
+ // tabs unindentation
_remove_text(cursor.line, cc - 1, cursor.line, cc);
if (cursor.column >= left)
cursor_set_column(MAX(0, cursor.column - 1));
update();
} else {
- int n = 0;
-
- for (int i = 1; i <= MIN(cc, indent_size); i++) {
- if (line[cc - i] != ' ') {
- break;
- }
- n++;
- }
-
- if (n > 0) {
- _remove_text(cursor.line, cc - n, cursor.line, cc);
- if (cursor.column > left - n) // inside text?
- cursor_set_column(MAX(0, cursor.column - n));
+ // spaces unindentation
+ int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
+ if (spaces_to_remove > 0) {
+ _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc);
+ if (cursor.column > left - spaces_to_remove) // inside text?
+ cursor_set_column(MAX(0, cursor.column - spaces_to_remove));
update();
}
}
@@ -2439,9 +2602,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
} else {
- //simple indent
+ // simple indent
if (indent_using_spaces) {
- _insert_text_at_cursor(space_indent);
+ // insert only as much spaces as needed till next indentation level
+ int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column);
+ String indent_to_insert = String();
+ for (int i = 0; i < spaces_to_add; i++)
+ indent_to_insert = ' ' + indent_to_insert;
+ _insert_text_at_cursor(indent_to_insert);
} else {
_insert_text_at_cursor("\t");
}
@@ -2517,7 +2685,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_left
+ FALLTHROUGH;
}
case KEY_LEFT: {
@@ -2532,9 +2700,22 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
#ifdef APPLE_STYLE_KEYS
if (k->get_command()) {
- cursor_set_column(0);
+ // Start at first column (it's slightly faster that way) and look for the first non-whitespace character.
+ int new_cursor_pos = 0;
+ for (int i = 0; i < text[cursor.line].length(); ++i) {
+ if (!_is_whitespace(text[cursor.line][i])) {
+ new_cursor_pos = i;
+ break;
+ }
+ }
+ if (new_cursor_pos == cursor.column) {
+ // We're already at the first text character, so move to the very beginning of the line.
+ cursor_set_column(0);
+ } else {
+ // We're somewhere to the right of the first text character; move to the first one.
+ cursor_set_column(new_cursor_pos);
+ }
} else if (k->get_alt()) {
-
#else
if (k->get_alt()) {
scancode_handled = false;
@@ -2580,7 +2761,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_right
+ FALLTHROUGH;
}
case KEY_RIGHT: {
@@ -2641,7 +2822,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_up
+ FALLTHROUGH;
}
case KEY_UP: {
@@ -2694,7 +2875,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_down
+ FALLTHROUGH;
}
case KEY_DOWN: {
@@ -2817,11 +2998,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_home
+ FALLTHROUGH;
}
-#ifdef APPLE_STYLE_KEYS
case KEY_HOME: {
-
+#ifdef APPLE_STYLE_KEYS
if (k->get_shift())
_pre_shift_selection();
@@ -2831,11 +3011,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_post_shift_selection();
else if (k->get_command() || k->get_control())
deselect();
-
- } break;
#else
- case KEY_HOME: {
-
if (k->get_shift())
_pre_shift_selection();
@@ -2876,19 +3052,17 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
deselect();
_cancel_completion();
completion_hint = "";
-
- } break;
#endif
+ } break;
case KEY_KP_1: {
if (k->get_unicode() != 0) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_end
+ FALLTHROUGH;
}
-#ifdef APPLE_STYLE_KEYS
case KEY_END: {
-
+#ifdef APPLE_STYLE_KEYS
if (k->get_shift())
_pre_shift_selection();
@@ -2898,11 +3072,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_post_shift_selection();
else if (k->get_command() || k->get_control())
deselect();
-
- } break;
#else
- case KEY_END: {
-
if (k->get_shift())
_pre_shift_selection();
@@ -2929,15 +3099,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_cancel_completion();
completion_hint = "";
-
- } break;
#endif
+ } break;
case KEY_KP_9: {
if (k->get_unicode() != 0) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_pageup
+ FALLTHROUGH;
}
case KEY_PAGEUP: {
@@ -2960,7 +3129,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_pagedown
+ FALLTHROUGH;
}
case KEY_PAGEDOWN: {
@@ -3139,21 +3308,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (scancode_handled)
accept_event();
- /*
- if (!scancode_handled && !k->get_command() && !k->get_alt()) {
- if (k->get_unicode()>=32) {
-
- if (readonly)
- break;
-
- accept_event();
- } else {
-
- break;
- }
- }
-*/
if (k->get_scancode() == KEY_INSERT) {
set_insert_mode(!insert_mode);
accept_event();
@@ -3196,7 +3351,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
end_complex_operation();
}
accept_event();
- } else {
}
}
@@ -3395,8 +3549,11 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
if (shift_first_line) {
text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line));
text.set_hidden(p_line + 1, text.is_hidden(p_line));
+ text.set_info_icon(p_line + 1, text.get_info_icon(p_line), text.get_info(p_line));
+
text.set_breakpoint(p_line, false);
text.set_hidden(p_line, false);
+ text.set_info_icon(p_line, NULL, "");
}
text.set_line_wrap_amount(p_line, -1);
@@ -3505,7 +3662,7 @@ void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r
op.chain_forward = false;
op.chain_backward = false;
- //see if it shold just be set as current op
+ //see if it should just be set as current op
if (current_op.type != op.type) {
op.prev_version = get_version();
_push_current_op();
@@ -3556,7 +3713,7 @@ void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, i
op.chain_forward = false;
op.chain_backward = false;
- //see if it shold just be set as current op
+ //see if it should just be set as current op
if (current_op.type != op.type) {
op.prev_version = get_version();
_push_current_op();
@@ -3649,7 +3806,7 @@ int TextEdit::get_total_visible_rows() const {
void TextEdit::_update_wrap_at() {
- wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset;
+ wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - wrap_right_offset;
update_cursor_wrap_offset();
text.clear_wrap_cache();
@@ -3683,7 +3840,7 @@ void TextEdit::adjust_viewport_to_cursor() {
set_line_as_last_visible(cur_line, cur_wrap);
}
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
@@ -3714,7 +3871,7 @@ void TextEdit::center_viewport_to_cursor() {
unfold_line(cursor.line);
set_line_as_center_visible(cursor.line, get_cursor_wrap_index());
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
@@ -4012,7 +4169,7 @@ void TextEdit::_scroll_moved(double p_to_val) {
int v_scroll_i = floor(get_v_scroll());
int sc = 0;
int n_line;
- for (n_line = 0; n_line < text.size(); n_line++) {
+ for (n_line = 0; n_line < text.size() - 1; n_line++) {
if (!is_line_hidden(n_line)) {
sc++;
sc += times_line_wraps(n_line);
@@ -4153,7 +4310,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
if (highlighted_word != String())
return CURSOR_POINTING_HAND;
- int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width;
+ int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
if ((completion_active && completion_rect.has_point(p_pos))) {
return CURSOR_ARROW;
}
@@ -4164,18 +4321,27 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
// breakpoint icon
- if (draw_breakpoint_gutter && p_pos.x > left_margin && p_pos.x <= left_margin + cache.breakpoint_gutter_width + 3) {
+ if (draw_breakpoint_gutter && p_pos.x > left_margin - 6 && p_pos.x <= left_margin + cache.breakpoint_gutter_width - 3) {
return CURSOR_POINTING_HAND;
}
+ // info icons
+ int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.info_gutter_width;
+ if (draw_info_gutter && p_pos.x > left_margin + cache.breakpoint_gutter_width - 6 && p_pos.x <= gutter_left - 3) {
+ if (text.has_info_icon(row)) {
+ return CURSOR_POINTING_HAND;
+ }
+ return CURSOR_ARROW;
+ }
+
// fold icon
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w;
- if (draw_fold_gutter && p_pos.x > gutter_left - 6 && p_pos.x <= gutter_left + cache.fold_gutter_width - 3) {
+ if (draw_fold_gutter && p_pos.x > gutter_left + cache.line_number_w - 6 && p_pos.x <= gutter_left + cache.line_number_w + cache.fold_gutter_width - 3) {
if (is_folded(row) || can_fold(row))
return CURSOR_POINTING_HAND;
else
return CURSOR_ARROW;
}
+
return CURSOR_ARROW;
} else {
int row, col;
@@ -4183,7 +4349,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
// eol fold icon
if (is_folded(row)) {
int line_width = text.get_line_width(row);
- line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
+ line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs;
if (p_pos.x > line_width - 3 && p_pos.x <= line_width + cache.folded_eol_icon->get_width() + 3) {
return CURSOR_POINTING_HAND;
}
@@ -4196,17 +4362,22 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
void TextEdit::set_text(String p_text) {
setting_text = true;
- _clear();
- _insert_text_at_cursor(p_text);
- clear_undo_history();
- cursor.column = 0;
- cursor.line = 0;
- cursor.x_ofs = 0;
- cursor.line_ofs = 0;
- cursor.wrap_ofs = 0;
- cursor.last_fit_x = 0;
- cursor_set_line(0);
- cursor_set_column(0);
+ if (!undo_enabled) {
+ _clear();
+ _insert_text_at_cursor(p_text);
+ }
+
+ if (undo_enabled) {
+ cursor_set_line(0);
+ cursor_set_column(0);
+
+ begin_complex_operation();
+ _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0));
+ _insert_text_at_cursor(p_text);
+ end_complex_operation();
+ selection.active = false;
+ }
+
update();
setting_text = false;
@@ -4303,7 +4474,27 @@ void TextEdit::clear() {
void TextEdit::set_readonly(bool p_readonly) {
+ if (readonly == p_readonly)
+ return;
+
readonly = p_readonly;
+
+ // Reorganize context menu.
+ menu->clear();
+ if (!readonly)
+ menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
+ menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
+ if (!readonly)
+ menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
+ menu->add_separator();
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
+ if (!readonly) {
+ menu->add_item(RTR("Clear"), MENU_CLEAR);
+ menu->add_separator();
+ menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
+ menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
+ }
+
update();
}
@@ -4372,7 +4563,9 @@ void TextEdit::_update_caches() {
cache.mark_color = get_color("mark_color");
cache.current_line_color = get_color("current_line_color");
cache.line_length_guideline_color = get_color("line_length_guideline_color");
+ cache.bookmark_color = get_color("bookmark_color");
cache.breakpoint_color = get_color("breakpoint_color");
+ cache.executing_line_color = get_color("executing_line_color");
cache.code_folding_color = get_color("code_folding_color");
cache.brace_mismatch_color = get_color("brace_mismatch_color");
cache.word_highlighted_color = get_color("word_highlighted_color");
@@ -4387,9 +4580,11 @@ void TextEdit::_update_caches() {
#endif
cache.row_height = cache.font->get_height() + cache.line_spacing;
cache.tab_icon = get_icon("tab");
- cache.folded_icon = get_icon("GuiTreeArrowRight", "EditorIcons");
- cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons");
+ cache.space_icon = get_icon("space");
+ cache.folded_icon = get_icon("folded");
+ cache.can_fold_icon = get_icon("fold");
cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons");
+ cache.executing_icon = get_icon("MainPlay", "EditorIcons");
text.set_font(cache.font);
if (syntax_highlighter) {
@@ -4955,6 +5150,48 @@ bool TextEdit::is_line_set_as_safe(int p_line) const {
return text.is_safe(p_line);
}
+void TextEdit::set_executing_line(int p_line) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ executing_line = p_line;
+ update();
+}
+
+void TextEdit::clear_executing_line() {
+ executing_line = -1;
+ update();
+}
+
+bool TextEdit::is_line_set_as_bookmark(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ return text.is_bookmark(p_line);
+}
+
+void TextEdit::set_line_as_bookmark(int p_line, bool p_bookmark) {
+
+ ERR_FAIL_INDEX(p_line, text.size());
+ text.set_bookmark(p_line, p_bookmark);
+ update();
+}
+
+void TextEdit::get_bookmarks(List<int> *p_bookmarks) const {
+
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_bookmark(i))
+ p_bookmarks->push_back(i);
+ }
+}
+
+Array TextEdit::get_bookmarks_array() const {
+
+ Array arr;
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_bookmark(i))
+ arr.append(i);
+ }
+ return arr;
+}
+
bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);
@@ -4994,6 +5231,19 @@ void TextEdit::remove_breakpoints() {
}
}
+void TextEdit::set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ text.set_info_icon(p_line, p_icon, p_info);
+ update();
+}
+
+void TextEdit::clear_info_icons() {
+ for (int i = 0; i < text.size(); i++) {
+ text.set_info_icon(i, NULL, "");
+ }
+ update();
+}
+
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -5210,6 +5460,17 @@ bool TextEdit::is_folded(int p_line) const {
return false;
}
+Vector<int> TextEdit::get_folded_lines() const {
+ Vector<int> folded_lines;
+
+ for (int i = 0; i < text.size(); i++) {
+ if (is_folded(i)) {
+ folded_lines.push_back(i);
+ }
+ }
+ return folded_lines;
+}
+
void TextEdit::fold_line(int p_line) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -5350,6 +5611,9 @@ void TextEdit::undo() {
TextOperation op = undo_stack_pos->get();
_do_text_op(op, true);
+ if (op.from_line != op.to_line || op.to_column != op.from_column + 1)
+ select(op.from_line, op.from_column, op.to_line, op.to_column);
+
current_op.version = op.prev_version;
if (undo_stack_pos->get().chain_backward) {
while (true) {
@@ -5476,6 +5740,7 @@ int TextEdit::get_indent_size() {
void TextEdit::set_draw_tabs(bool p_draw) {
draw_tabs = p_draw;
+ update();
}
bool TextEdit::is_drawing_tabs() const {
@@ -5483,6 +5748,16 @@ bool TextEdit::is_drawing_tabs() const {
return draw_tabs;
}
+void TextEdit::set_draw_spaces(bool p_draw) {
+
+ draw_spaces = p_draw;
+}
+
+bool TextEdit::is_drawing_spaces() const {
+
+ return draw_spaces;
+}
+
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
@@ -5662,19 +5937,29 @@ void TextEdit::_confirm_completion() {
cursor_set_column(cursor.column - completion_base.length(), false);
insert_text_at_cursor(completion_current);
- // When inserted into the middle of an existing string, don't add an unnecessary quote
+ // When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket.
String line = text[cursor.line];
CharType next_char = line[cursor.column];
CharType last_completion_char = completion_current[completion_current.length() - 1];
- if ((last_completion_char == '"' || last_completion_char == '\'') &&
- last_completion_char == next_char) {
+ if ((last_completion_char == '"' || last_completion_char == '\'') && last_completion_char == next_char) {
_base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
}
- if (last_completion_char == '(' && auto_brace_completion_enabled) {
- insert_text_at_cursor(")");
- cursor.column--;
+ if (last_completion_char == '(') {
+
+ if (next_char == last_completion_char) {
+ _base_remove_text(cursor.line, cursor.column - 1, cursor.line, cursor.column);
+ } else if (auto_brace_completion_enabled) {
+ insert_text_at_cursor(")");
+ cursor.column--;
+ }
+ } else if (last_completion_char == ')' && next_char == '(') {
+
+ _base_remove_text(cursor.line, cursor.column - 2, cursor.line, cursor.column);
+ if (line[cursor.column + 1] != ')') {
+ cursor.column--;
+ }
}
end_complex_operation();
@@ -5718,6 +6003,7 @@ void TextEdit::_update_completion_candidates() {
bool inquote = false;
int first_quote = -1;
+ int restore_quotes = -1;
int c = cofs - 1;
while (c >= 0) {
@@ -5725,6 +6011,11 @@ void TextEdit::_update_completion_candidates() {
inquote = !inquote;
if (first_quote == -1)
first_quote = c;
+ restore_quotes = 0;
+ } else if (restore_quotes == 0 && l[c] == '$') {
+ restore_quotes = 1;
+ } else if (restore_quotes == 0 && !_is_whitespace(l[c])) {
+ restore_quotes = -1;
}
c--;
}
@@ -5792,6 +6083,11 @@ void TextEdit::_update_completion_candidates() {
completion_strings.write[i] = completion_strings[i].unquote().quote("'");
}
+ if (inquote && restore_quotes == 1 && !completion_strings[i].is_quoted()) {
+ String quote = single_quote ? "'" : "\"";
+ completion_strings.write[i] = completion_strings[i].quote(quote);
+ }
+
if (completion_strings[i].begins_with(s)) {
completion_options.push_back(completion_strings[i]);
} else if (completion_strings[i].to_lower().begins_with(s.to_lower())) {
@@ -5869,7 +6165,6 @@ void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) {
completion_current = "";
completion_index = 0;
_update_completion_candidates();
- //
}
String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
@@ -5984,6 +6279,15 @@ void TextEdit::set_line_length_guideline_column(int p_column) {
update();
}
+void TextEdit::set_bookmark_gutter_enabled(bool p_draw) {
+ draw_bookmark_gutter = p_draw;
+ update();
+}
+
+bool TextEdit::is_bookmark_gutter_enabled() const {
+ return draw_bookmark_gutter;
+}
+
void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
draw_breakpoint_gutter = p_draw;
update();
@@ -6020,6 +6324,24 @@ int TextEdit::get_fold_gutter_width() const {
return cache.fold_gutter_width;
}
+void TextEdit::set_draw_info_gutter(bool p_draw) {
+ draw_info_gutter = p_draw;
+ update();
+}
+
+bool TextEdit::is_drawing_info_gutter() const {
+ return draw_info_gutter;
+}
+
+void TextEdit::set_info_gutter_width(int p_gutter_width) {
+ info_gutter_width = p_gutter_width;
+ update();
+}
+
+int TextEdit::get_info_gutter_width() const {
+ return info_gutter_width;
+}
+
void TextEdit::set_hiding_enabled(int p_enabled) {
if (!p_enabled)
unhide_all_lines();
@@ -6176,8 +6498,14 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
+ ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
+ ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
+ ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces);
+ ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces);
ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
+ ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &TextEdit::is_drawing_fold_gutter);
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -6224,7 +6552,10 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -6245,6 +6576,7 @@ void TextEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_completion"));
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row")));
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column")));
+ ADD_SIGNAL(MethodInfo("info_clicked", PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::STRING, "info")));
BIND_ENUM_CONSTANT(MENU_CUT);
BIND_ENUM_CONSTANT(MENU_COPY);
@@ -6261,9 +6593,9 @@ void TextEdit::_bind_methods() {
TextEdit::TextEdit() {
- readonly = false;
setting_row = false;
draw_tabs = false;
+ draw_spaces = false;
override_selected_font_color = false;
draw_caret = true;
max_chars = 0;
@@ -6280,6 +6612,8 @@ TextEdit::TextEdit() {
breakpoint_gutter_width = 0;
cache.fold_gutter_width = 0;
fold_gutter_width = 0;
+ info_gutter_width = 0;
+ cache.info_gutter_width = 0;
set_default_cursor_shape(CURSOR_IBEAM);
indent_size = 4;
@@ -6350,8 +6684,10 @@ TextEdit::TextEdit() {
line_numbers_zero_padded = false;
line_length_guideline = false;
line_length_guideline_col = 80;
+ draw_bookmark_gutter = false;
draw_breakpoint_gutter = false;
draw_fold_gutter = false;
+ draw_info_gutter = false;
hiding_enabled = false;
next_operation_is_complex = false;
scroll_past_end_of_file_enabled = false;
@@ -6373,16 +6709,12 @@ TextEdit::TextEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
- menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
- menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
- menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
- menu->add_separator();
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
- menu->add_item(RTR("Clear"), MENU_CLEAR);
- menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
- menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
+ readonly = true; // initialise to opposite first, so we get past the early-out in set_readonly
+ set_readonly(false);
menu->connect("id_pressed", this, "menu_option");
+ first_draw = true;
+
+ executing_line = -1;
}
TextEdit::~TextEdit() {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 95f1fbbee5..68e590f1e6 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -75,10 +75,13 @@ public:
int width_cache : 24;
bool marked : 1;
bool breakpoint : 1;
+ bool bookmark : 1;
bool hidden : 1;
bool safe : 1;
int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
+ Ref<Texture> info_icon;
+ String info;
String data;
};
@@ -103,12 +106,21 @@ public:
void set(int p_line, const String &p_text);
void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; }
bool is_marked(int p_line) const { return text[p_line].marked; }
+ void set_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; }
+ bool is_bookmark(int p_line) const { return text[p_line].bookmark; }
void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; }
bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; }
bool is_safe(int p_line) const { return text[p_line].safe; }
+ void set_info_icon(int p_line, Ref<Texture> p_icon, String p_info) {
+ text.write[p_line].info_icon = p_icon;
+ text.write[p_line].info = p_info;
+ }
+ bool has_info_icon(int p_line) const { return text[p_line].info_icon.is_valid(); }
+ const Ref<Texture> &get_info_icon(int p_line) const { return text[p_line].info_icon; }
+ const String &get_info(int p_line) const { return text[p_line].info; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
@@ -154,9 +166,11 @@ private:
struct Cache {
Ref<Texture> tab_icon;
+ Ref<Texture> space_icon;
Ref<Texture> can_fold_icon;
Ref<Texture> folded_icon;
Ref<Texture> folded_eol_icon;
+ Ref<Texture> executing_icon;
Ref<StyleBox> style_normal;
Ref<StyleBox> style_focus;
Ref<StyleBox> style_readonly;
@@ -177,7 +191,9 @@ private:
Color member_variable_color;
Color selection_color;
Color mark_color;
+ Color bookmark_color;
Color breakpoint_color;
+ Color executing_line_color;
Color code_folding_color;
Color current_line_color;
Color line_length_guideline_color;
@@ -193,6 +209,7 @@ private:
int line_number_w;
int breakpoint_gutter_width;
int fold_gutter_width;
+ int info_gutter_width;
} cache;
Map<int, int> color_region_cache;
@@ -275,8 +292,10 @@ private:
int wrap_at;
int wrap_right_offset;
+ bool first_draw;
bool setting_row;
bool draw_tabs;
+ bool draw_spaces;
bool override_selected_font_color;
bool cursor_changed_dirty;
bool text_changed_dirty;
@@ -285,11 +304,14 @@ private:
bool line_numbers_zero_padded;
bool line_length_guideline;
int line_length_guideline_col;
+ bool draw_bookmark_gutter;
bool draw_breakpoint_gutter;
int breakpoint_gutter_width;
bool draw_fold_gutter;
int fold_gutter_width;
bool hiding_enabled;
+ bool draw_info_gutter;
+ int info_gutter_width;
bool highlight_all_occurrences;
bool scroll_past_end_of_file_enabled;
@@ -332,6 +354,8 @@ private:
bool context_menu_enabled;
+ int executing_line;
+
int get_visible_rows() const;
int get_total_visible_rows() const;
@@ -413,6 +437,9 @@ private:
void _confirm_completion();
void _update_completion_candidates();
+ int _calculate_spaces_till_next_left_indent(int column);
+ int _calculate_spaces_till_next_right_indent(int column);
+
protected:
virtual String get_tooltip(const Point2 &p_pos) const;
@@ -471,14 +498,23 @@ public:
void insert_at(const String &p_text, int at);
int get_line_count() const;
void set_line_as_marked(int p_line, bool p_marked);
+ void set_line_as_bookmark(int p_line, bool p_bookmark);
+ bool is_line_set_as_bookmark(int p_line) const;
+ void get_bookmarks(List<int> *p_bookmarks) const;
+ Array get_bookmarks_array() const;
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(int p_line) const;
+ void set_executing_line(int p_line);
+ void clear_executing_line();
void set_line_as_safe(int p_line, bool p_safe);
bool is_line_set_as_safe(int p_line) const;
void get_breakpoints(List<int> *p_breakpoints) const;
Array get_breakpoints_array() const;
void remove_breakpoints();
+ void set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info = "");
+ void clear_info_icons();
+
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
void fold_all_lines();
@@ -489,6 +525,7 @@ public:
bool can_fold(int p_line) const;
bool is_folded(int p_line) const;
+ Vector<int> get_folded_lines() const;
void fold_line(int p_line);
void unfold_line(int p_line);
void toggle_fold_line(int p_line);
@@ -590,6 +627,8 @@ public:
int get_indent_size();
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
+ void set_draw_spaces(bool p_draw);
+ bool is_drawing_spaces() const;
void set_override_selected_font_color(bool p_override_selected_font_color);
bool is_overriding_selected_font_color() const;
@@ -637,6 +676,9 @@ public:
void set_show_line_length_guideline(bool p_show);
void set_line_length_guideline_column(int p_column);
+ void set_bookmark_gutter_enabled(bool p_draw);
+ bool is_bookmark_gutter_enabled() const;
+
void set_breakpoint_gutter_enabled(bool p_draw);
bool is_breakpoint_gutter_enabled() const;
@@ -649,6 +691,12 @@ public:
void set_fold_gutter_width(int p_gutter_width);
int get_fold_gutter_width() const;
+ void set_draw_info_gutter(bool p_draw);
+ bool is_drawing_info_gutter() const;
+
+ void set_info_gutter_width(int p_gutter_width);
+ int get_info_gutter_width() const;
+
void set_hiding_enabled(int p_enabled);
int is_hiding_enabled() const;
@@ -683,10 +731,11 @@ protected:
TextEdit *text_editor;
public:
+ virtual ~SyntaxHighlighter() {}
virtual void _update_cache() = 0;
virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
- virtual String get_name() = 0;
+ virtual String get_name() const = 0;
virtual List<String> get_supported_languages() = 0;
void set_text_editor(TextEdit *p_text_editor);
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 19bef9fdf5..b5f949aeb7 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -64,7 +64,7 @@ bool TextureButton::has_point(const Point2 &p_point) const {
Rect2 rect = Rect2();
Size2 mask_size = click_mask->get_size();
- if (_position_rect.no_area()) {
+ if (_position_rect.has_no_area()) {
rect.size = mask_size;
} else if (_tile) {
// if the stretch mode is tile we offset the point to keep it inside the mask size
@@ -223,8 +223,7 @@ void TextureButton::_notification(int p_what) {
}
if (has_focus() && focused.is_valid()) {
- Rect2 drect(Point2(), get_size());
- draw_texture_rect(focused, drect, false);
+ draw_texture_rect(focused, _position_rect, false);
};
} break;
}
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index caae48336b..423794d8ba 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -38,30 +38,32 @@ void TextureRect::_notification(int p_what) {
if (texture.is_null())
return;
+ Size2 size;
+ Point2 offset;
+ Rect2 region;
+ bool tile = false;
+
switch (stretch_mode) {
case STRETCH_SCALE_ON_EXPAND: {
- Size2 s = expand ? get_size() : texture->get_size();
- draw_texture_rect(texture, Rect2(Point2(), s), false);
+ size = expand ? get_size() : texture->get_size();
} break;
case STRETCH_SCALE: {
- draw_texture_rect(texture, Rect2(Point2(), get_size()), false);
+ size = get_size();
} break;
case STRETCH_TILE: {
- draw_texture_rect(texture, Rect2(Point2(), get_size()), true);
+ size = get_size();
+ tile = true;
} break;
case STRETCH_KEEP: {
- draw_texture_rect(texture, Rect2(Point2(), texture->get_size()), false);
-
+ size = texture->get_size();
} break;
case STRETCH_KEEP_CENTERED: {
-
- Vector2 ofs = (get_size() - texture->get_size()) / 2;
- draw_texture_rect(texture, Rect2(ofs, texture->get_size()), false);
+ offset = (get_size() - texture->get_size()) / 2;
+ size = texture->get_size();
} break;
case STRETCH_KEEP_ASPECT_CENTERED:
case STRETCH_KEEP_ASPECT: {
-
- Size2 size = get_size();
+ size = get_size();
int tex_width = texture->get_width() * size.height / texture->get_height();
int tex_height = size.height;
@@ -70,26 +72,35 @@ void TextureRect::_notification(int p_what) {
tex_height = texture->get_height() * tex_width / texture->get_width();
}
- int ofs_x = 0;
- int ofs_y = 0;
-
if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
- ofs_x += (size.width - tex_width) / 2;
- ofs_y += (size.height - tex_height) / 2;
+ offset.x += (size.width - tex_width) / 2;
+ offset.y += (size.height - tex_height) / 2;
}
- draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height));
+ size.width = tex_width;
+ size.height = tex_height;
} break;
case STRETCH_KEEP_ASPECT_COVERED: {
- Size2 size = get_size();
+ size = get_size();
+
Size2 tex_size = texture->get_size();
Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height);
float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height;
Size2 scaledTexSize = tex_size * scale;
- Point2 ofs = ((scaledTexSize - size) / scale).abs() / 2.0f;
- draw_texture_rect_region(texture, Rect2(Point2(), size), Rect2(ofs, size / scale));
+
+ region.position = ((scaledTexSize - size) / scale).abs() / 2.0f;
+ region.size = size / scale;
} break;
}
+
+ size.width *= hflip ? -1.0f : 1.0f;
+ size.height *= vflip ? -1.0f : 1.0f;
+
+ if (region.has_no_area()) {
+ draw_texture_rect(texture, Rect2(offset, size), tile);
+ } else {
+ draw_texture_rect_region(texture, Rect2(offset, size), region);
+ }
}
}
@@ -106,12 +117,18 @@ void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_texture"), &TextureRect::get_texture);
ClassDB::bind_method(D_METHOD("set_expand", "enable"), &TextureRect::set_expand);
ClassDB::bind_method(D_METHOD("has_expand"), &TextureRect::has_expand);
+ ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureRect::set_flip_h);
+ ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureRect::is_flipped_h);
+ ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureRect::set_flip_v);
+ ClassDB::bind_method(D_METHOD("is_flipped_v"), &TextureRect::is_flipped_v);
ClassDB::bind_method(D_METHOD("set_stretch_mode", "stretch_mode"), &TextureRect::set_stretch_mode);
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureRect::get_stretch_mode);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
BIND_ENUM_CONSTANT(STRETCH_SCALE_ON_EXPAND);
BIND_ENUM_CONSTANT(STRETCH_SCALE);
@@ -161,9 +178,31 @@ TextureRect::StretchMode TextureRect::get_stretch_mode() const {
return stretch_mode;
}
+void TextureRect::set_flip_h(bool p_flip) {
+
+ hflip = p_flip;
+ update();
+}
+bool TextureRect::is_flipped_h() const {
+
+ return hflip;
+}
+
+void TextureRect::set_flip_v(bool p_flip) {
+
+ vflip = p_flip;
+ update();
+}
+bool TextureRect::is_flipped_v() const {
+
+ return vflip;
+}
+
TextureRect::TextureRect() {
expand = false;
+ hflip = false;
+ vflip = false;
set_mouse_filter(MOUSE_FILTER_PASS);
stretch_mode = STRETCH_SCALE_ON_EXPAND;
}
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index ddd101573b..3ab35324e5 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -53,6 +53,8 @@ public:
private:
bool expand;
+ bool hflip;
+ bool vflip;
Ref<Texture> texture;
StretchMode stretch_mode;
@@ -71,6 +73,12 @@ public:
void set_stretch_mode(StretchMode p_mode);
StretchMode get_stretch_mode() const;
+ void set_flip_h(bool p_flip);
+ bool is_flipped_h() const;
+
+ void set_flip_v(bool p_flip);
+ bool is_flipped_v() const;
+
TextureRect();
~TextureRect();
};
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 49cad6fccf..474eb51860 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -29,7 +29,6 @@
/*************************************************************************/
#include "tree.h"
-#include <limits.h>
#include "core/math/math_funcs.h"
#include "core/os/input.h"
@@ -43,6 +42,8 @@
#include "editor/editor_node.h"
#endif
+#include <limits.h>
+
void TreeItem::move_to_top() {
if (!parent || parent->children == this)
@@ -388,7 +389,7 @@ TreeItem *TreeItem::get_children() {
return children;
}
-TreeItem *TreeItem::get_prev_visible() {
+TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
TreeItem *current = this;
@@ -397,8 +398,20 @@ TreeItem *TreeItem::get_prev_visible() {
if (!prev) {
current = current->parent;
- if (!current || (current == tree->root && tree->hide_root))
+ if (current == tree->root && tree->hide_root) {
return NULL;
+ } else if (!current) {
+ if (p_wrap) {
+ current = this;
+ TreeItem *temp = this->get_next_visible();
+ while (temp) {
+ current = temp;
+ temp = temp->get_next_visible();
+ }
+ } else {
+ return NULL;
+ }
+ }
} else {
current = prev;
@@ -414,7 +427,7 @@ TreeItem *TreeItem::get_prev_visible() {
return current;
}
-TreeItem *TreeItem::get_next_visible() {
+TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *current = this;
@@ -432,10 +445,14 @@ TreeItem *TreeItem::get_next_visible() {
current = current->parent;
}
- if (current == NULL)
- return NULL;
- else
+ if (!current) {
+ if (p_wrap)
+ return tree->root;
+ else
+ return NULL;
+ } else {
current = current->next;
+ }
}
return current;
@@ -739,8 +756,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
- ClassDB::bind_method(D_METHOD("get_next_visible"), &TreeItem::get_next_visible);
- ClassDB::bind_method(D_METHOD("get_prev_visible"), &TreeItem::get_prev_visible);
+ ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
@@ -940,6 +957,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
int check_icon_h = cache.checked->get_height();
if (height < check_icon_h)
height = check_icon_h;
+ FALLTHROUGH;
}
case TreeItem::CELL_MODE_STRING:
case TreeItem::CELL_MODE_CUSTOM:
@@ -960,7 +978,8 @@ int Tree::compute_item_height(TreeItem *p_item) const {
}
} break;
- default: {}
+ default: {
+ }
}
}
int item_min_height = p_item->get_custom_minimum_height();
@@ -1967,6 +1986,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
item_h += child_h;
}
}
+ if (p_item == root && p_button == BUTTON_RIGHT) {
+ emit_signal("empty_rmb", get_local_mouse_position());
+ }
}
return item_h; // nothing found
@@ -2018,7 +2040,9 @@ void Tree::text_editor_enter(String p_text) {
//popup_edited_item->edited_signal.call( popup_edited_item_col );
} break;
- default: { ERR_FAIL(); }
+ default: {
+ ERR_FAIL();
+ }
}
item_edited(popup_edited_item_col, popup_edited_item);
@@ -2261,7 +2285,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
next = _n;
} else {
- return;
+ break;
}
}
if (next == selected_item)
@@ -2299,7 +2323,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
prev = _n;
} else {
- return;
+ break;
}
}
if (prev == selected_item)
@@ -2845,7 +2869,7 @@ void Tree::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAG_BEGIN) {
single_select_defer = NULL;
- if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) {
+ if (cache.scroll_speed > 0) {
scrolling = true;
set_physics_process_internal(true);
}
@@ -2892,22 +2916,22 @@ void Tree::_notification(int p_what) {
}
}
- if (scrolling) {
- Point2 point = get_viewport()->get_mouse_position() - get_global_position();
- if (point.x < cache.scroll_border) {
- point.x -= cache.scroll_border;
- } else if (point.x > get_size().width - cache.scroll_border) {
- point.x -= get_size().width - cache.scroll_border;
- } else {
- point.x = 0;
+ Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
+ if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) {
+ Point2 point;
+
+ if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) {
+ point.x = mouse_position.x - cache.scroll_border;
+ } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) {
+ point.x = mouse_position.x - (get_size().width - cache.scroll_border);
}
- if (point.y < cache.scroll_border) {
- point.y -= cache.scroll_border;
- } else if (point.y > get_size().height - cache.scroll_border) {
- point.y -= get_size().height - cache.scroll_border;
- } else {
- point.y = 0;
+
+ if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) {
+ point.y = mouse_position.y - cache.scroll_border;
+ } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) {
+ point.y = mouse_position.y - (get_size().height - cache.scroll_border);
}
+
point *= cache.scroll_speed * get_physics_process_delta_time();
point += get_scroll();
h_scroll->set_value(point.x);
@@ -3383,10 +3407,13 @@ void Tree::ensure_cursor_is_visible() {
int h = compute_item_height(selected) + cache.vseparation;
int screenh = get_size().height - h_scroll->get_combined_minimum_size().height;
- if (ofs + h > v_scroll->get_value() + screenh)
+ if (h > screenh) { //screen size is too small, maybe it was not resized yet.
+ v_scroll->set_value(ofs);
+ } else if (ofs + h > v_scroll->get_value() + screenh) {
v_scroll->call_deferred("set_value", ofs - screenh + h);
- else if (ofs < v_scroll->get_value())
+ } else if (ofs < v_scroll->get_value()) {
v_scroll->set_value(ofs);
+ }
}
int Tree::get_pressed_button() const {
@@ -3480,6 +3507,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
+ TreeItem *from = p_at;
while (p_at) {
for (int i = 0; i < columns.size(); i++) {
@@ -3491,9 +3519,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
}
if (p_backwards)
- p_at = p_at->get_prev_visible();
+ p_at = p_at->get_prev_visible(true);
else
- p_at = p_at->get_next_visible();
+ p_at = p_at->get_next_visible(true);
+
+ if ((p_at) == from)
+ break;
}
return NULL;
@@ -3501,10 +3532,14 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) {
+ TreeItem *from = get_selected()->get_next_visible();
+
if (!root)
+ from = root;
+ if (!from)
return NULL;
- return _search_item_text(root, p_find, r_col, p_selectable);
+ return _search_item_text(from, p_find, r_col, p_selectable);
}
void Tree::_do_incr_search(const String &p_add) {
@@ -3513,7 +3548,7 @@ void Tree::_do_incr_search(const String &p_add) {
uint64_t diff = time - last_keypress;
if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)))
incr_search = p_add;
- else
+ else if (incr_search != p_add)
incr_search += p_add;
last_keypress = time;
@@ -3845,6 +3880,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("cell_selected"));
ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected")));
ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::VECTOR2, "position")));
+ ADD_SIGNAL(MethodInfo("empty_rmb", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("empty_tree_rmb_selected", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("item_edited"));
ADD_SIGNAL(MethodInfo("item_rmb_edited"));
@@ -3930,7 +3966,6 @@ Tree::Tree() {
cache.click_item = NULL;
cache.click_column = 0;
cache.hover_cell = -1;
- cache.hover_index = -1;
last_keypress = 0;
focus_in_id = 0;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 26d64baafb..45d451eb72 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -238,8 +238,8 @@ public:
TreeItem *get_parent();
TreeItem *get_children();
- TreeItem *get_prev_visible();
- TreeItem *get_next_visible();
+ TreeItem *get_prev_visible(bool p_wrap = false);
+ TreeItem *get_next_visible(bool p_wrap = false);
void remove_child(TreeItem *p_item);
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 2b1991ebb0..8cab1b1280 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -153,6 +153,7 @@ void CanvasLayer::_notification(int p_what) {
VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
+ _update_follow_viewport();
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -160,6 +161,7 @@ void CanvasLayer::_notification(int p_what) {
vp->_canvas_layer_remove(this);
VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
+ _update_follow_viewport(false);
} break;
case NOTIFICATION_MOVED_IN_PARENT: {
@@ -235,6 +237,41 @@ RID CanvasLayer::get_canvas() const {
return canvas;
}
+
+void CanvasLayer::set_follow_viewport(bool p_enable) {
+ if (follow_viewport == p_enable) {
+ return;
+ }
+
+ follow_viewport = p_enable;
+ _update_follow_viewport();
+}
+
+bool CanvasLayer::is_following_viewport() const {
+ return follow_viewport;
+}
+
+void CanvasLayer::set_follow_viewport_scale(float p_ratio) {
+ follow_viewport_scale = p_ratio;
+ _update_follow_viewport();
+}
+
+float CanvasLayer::get_follow_viewport_scale() const {
+ return follow_viewport_scale;
+}
+
+void CanvasLayer::_update_follow_viewport(bool p_force_exit) {
+
+ if (!is_inside_tree()) {
+ return;
+ }
+ if (p_force_exit || !follow_viewport) {
+ VS::get_singleton()->canvas_set_parent(canvas, RID(), 1.0);
+ } else {
+ VS::get_singleton()->canvas_set_parent(canvas, vp->get_world_2d()->get_canvas(), follow_viewport_scale);
+ }
+}
+
void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
@@ -255,18 +292,30 @@ void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &CanvasLayer::set_scale);
ClassDB::bind_method(D_METHOD("get_scale"), &CanvasLayer::get_scale);
+ ClassDB::bind_method(D_METHOD("set_follow_viewport", "enable"), &CanvasLayer::set_follow_viewport);
+ ClassDB::bind_method(D_METHOD("is_following_viewport"), &CanvasLayer::is_following_viewport);
+
+ ClassDB::bind_method(D_METHOD("set_follow_viewport_scale", "scale"), &CanvasLayer::set_follow_viewport_scale);
+ ClassDB::bind_method(D_METHOD("get_follow_viewport_scale"), &CanvasLayer::get_follow_viewport_scale);
+
ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport);
ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport);
ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas);
+ ADD_GROUP("Layer", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer");
+ ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform"), "set_transform", "get_transform");
+ ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", 0), "set_custom_viewport", "get_custom_viewport");
+ ADD_GROUP("Follow Viewport", "follow_viewport");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale");
}
CanvasLayer::CanvasLayer() {
@@ -280,6 +329,8 @@ CanvasLayer::CanvasLayer() {
custom_viewport = NULL;
custom_viewport_id = 0;
sort_index = 0;
+ follow_viewport = false;
+ follow_viewport_scale = 1.0;
}
CanvasLayer::~CanvasLayer() {
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index 5d67245102..fa2558556c 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -55,8 +55,12 @@ class CanvasLayer : public Node {
int sort_index;
+ bool follow_viewport;
+ float follow_viewport_scale;
+
void _update_xform();
void _update_locrotscale();
+ void _update_follow_viewport(bool p_force_exit = false);
protected:
void _notification(int p_what);
@@ -91,6 +95,12 @@ public:
void reset_sort_index();
int get_sort_index();
+ void set_follow_viewport(bool p_enable);
+ bool is_following_viewport() const;
+
+ void set_follow_viewport_scale(float p_ratio);
+ float get_follow_viewport_scale() const;
+
RID get_canvas() const;
CanvasLayer();
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 8b68b3215c..e65314644e 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -286,7 +286,7 @@ bool HTTPRequest::_update_connection() {
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, PoolByteArray());
return true;
}
- if (got_response && body_len < 0) {
+ if (body_len < 0) {
// Chunked transfer is done
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
return true;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 04d7107fa4..2f23c11748 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -39,8 +39,14 @@
#include "scene/scene_string_names.h"
#include "viewport.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif
+
VARIANT_ENUM_CAST(Node::PauseMode);
+int Node::orphan_node_count = 0;
+
void Node::_notification(int p_notification) {
switch (p_notification) {
@@ -84,11 +90,14 @@ void Node::_notification(int p_notification) {
add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id()));
get_tree()->node_count++;
+ orphan_node_count--;
} break;
case NOTIFICATION_EXIT_TREE: {
get_tree()->node_count--;
+ orphan_node_count++;
+
if (data.input)
remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id()));
if (data.unhandled_input)
@@ -940,6 +949,7 @@ void Node::set_name(const String &p_name) {
if (is_inside_tree()) {
emit_signal("renamed");
+ get_tree()->node_renamed(this);
get_tree()->tree_changed();
}
}
@@ -1313,6 +1323,10 @@ Node *Node::_get_child_by_name(const StringName &p_name) const {
Node *Node::get_node_or_null(const NodePath &p_path) const {
+ if (p_path.is_empty()) {
+ return NULL;
+ }
+
if (!data.inside_tree && p_path.is_absolute()) {
ERR_EXPLAIN("Can't use get_node() with absolute paths from outside the active scene tree.");
ERR_FAIL_V(NULL);
@@ -2063,7 +2077,9 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
}
}
- node->set_name(get_name());
+ if (get_name() != String()) {
+ node->set_name(get_name());
+ }
#ifdef TOOLS_ENABLED
if ((p_flags & DUPLICATE_FROM_EDITOR) && r_duplimap)
@@ -2417,7 +2433,7 @@ void Node::_replace_connections_target(Node *p_new_target) {
if (c.flags & CONNECT_PERSIST) {
c.source->disconnect(c.signal, this, c.method);
- bool valid = p_new_target->has_method(c.method) || p_new_target->get_script().is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.method);
+ bool valid = p_new_target->has_method(c.method) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.method);
ERR_EXPLAIN("Attempt to connect signal \'" + c.source->get_class() + "." + c.signal + "\' to nonexistent method \'" + c.target->get_class() + "." + c.method + "\'");
ERR_CONTINUE(!valid);
c.source->connect(c.signal, p_new_target, c.method, c.binds, c.flags);
@@ -2628,10 +2644,16 @@ NodePath Node::get_import_path() const {
static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) {
+#ifdef TOOLS_ENABLED
+ const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+#else
+ const String quote_style = "\"";
+#endif
+
if (p_node != p_base && !p_node->get_owner())
return;
String n = p_base->get_path_to(p_node);
- r_options->push_back("\"" + n + "\"");
+ r_options->push_back(quote_style + n + quote_style);
for (int i = 0; i < p_node->get_child_count(); i++) {
_add_nodes_to_options(p_base, p_node->get_child(i), r_options);
}
@@ -2755,6 +2777,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process);
ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes);
ClassDB::bind_method(D_METHOD("get_position_in_parent"), &Node::get_position_in_parent);
+
ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded);
ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded);
@@ -2830,10 +2853,22 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_DRAG_BEGIN);
BIND_CONSTANT(NOTIFICATION_DRAG_END);
BIND_CONSTANT(NOTIFICATION_PATH_CHANGED);
- BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
+ BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER);
+ BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT);
+ BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN);
+ BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT);
+ BIND_CONSTANT(NOTIFICATION_WM_QUIT_REQUEST);
+ BIND_CONSTANT(NOTIFICATION_WM_GO_BACK_REQUEST);
+ BIND_CONSTANT(NOTIFICATION_WM_UNFOCUS_REQUEST);
+ BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
+ BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
+ BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
+ BIND_CONSTANT(NOTIFICATION_CRASH);
+ BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE);
+
BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT);
BIND_ENUM_CONSTANT(PAUSE_MODE_STOP);
BIND_ENUM_CONSTANT(PAUSE_MODE_PROCESS);
@@ -2855,7 +2890,12 @@ void Node::_bind_methods() {
//ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ;
ADD_GROUP("Pause", "pause_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded");
+
+#ifdef ENABLE_DEPRECATED
+ //no longer exists, but remains for compatibility (keep previous scenes folded
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", 0), "set_display_folded", "is_displayed_folded");
+#endif
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
@@ -2916,6 +2956,8 @@ Node::Node() {
data.use_placeholder = false;
data.display_folded = false;
data.ready_first = true;
+
+ orphan_node_count++;
}
Node::~Node() {
@@ -2926,6 +2968,8 @@ Node::~Node() {
ERR_FAIL_COND(data.parent);
ERR_FAIL_COND(data.children.size());
+
+ orphan_node_count--;
}
////////////////////////////////
diff --git a/scene/main/node.h b/scene/main/node.h
index e6189389cb..9b9ca06455 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -75,6 +75,8 @@ public:
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; }
};
+ static int orphan_node_count;
+
private:
struct GroupData {
@@ -216,6 +218,7 @@ protected:
public:
enum {
+
// you can make your own, but don't use the same numbers as other notifications in other nodes
NOTIFICATION_ENTER_TREE = 10,
NOTIFICATION_EXIT_TREE = 11,
@@ -231,10 +234,23 @@ public:
NOTIFICATION_DRAG_BEGIN = 21,
NOTIFICATION_DRAG_END = 22,
NOTIFICATION_PATH_CHANGED = 23,
- NOTIFICATION_TRANSLATION_CHANGED = 24,
+ //NOTIFICATION_TRANSLATION_CHANGED = 24, moved below
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
NOTIFICATION_POST_ENTER_TREE = 27,
+ //keep these linked to node
+ NOTIFICATION_WM_MOUSE_ENTER = MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
+ NOTIFICATION_WM_MOUSE_EXIT = MainLoop::NOTIFICATION_WM_MOUSE_EXIT,
+ NOTIFICATION_WM_FOCUS_IN = MainLoop::NOTIFICATION_WM_FOCUS_IN,
+ NOTIFICATION_WM_FOCUS_OUT = MainLoop::NOTIFICATION_WM_FOCUS_OUT,
+ NOTIFICATION_WM_QUIT_REQUEST = MainLoop::NOTIFICATION_WM_QUIT_REQUEST,
+ NOTIFICATION_WM_GO_BACK_REQUEST = MainLoop::NOTIFICATION_WM_GO_BACK_REQUEST,
+ NOTIFICATION_WM_UNFOCUS_REQUEST = MainLoop::NOTIFICATION_WM_UNFOCUS_REQUEST,
+ NOTIFICATION_OS_MEMORY_WARNING = MainLoop::NOTIFICATION_OS_MEMORY_WARNING,
+ NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED,
+ NOTIFICATION_WM_ABOUT = MainLoop::NOTIFICATION_WM_ABOUT,
+ NOTIFICATION_CRASH = MainLoop::NOTIFICATION_CRASH,
+ NOTIFICATION_OS_IME_UPDATE = MainLoop::NOTIFICATION_OS_IME_UPDATE
};
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 689f18a09d..65cda73a23 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -105,6 +105,11 @@ void SceneTree::node_removed(Node *p_node) {
call_skip.insert(p_node);
}
+void SceneTree::node_renamed(Node *p_node) {
+
+ emit_signal(node_renamed_name, p_node);
+}
+
SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) {
Map<StringName, Group>::Element *E = group_map.find(p_group);
@@ -369,8 +374,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
}
void SceneTree::call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) {
-
- call_group_flags(0, p_group, VARIANT_ARG_PASS);
+ call_group_flags(0, p_group, p_function, VARIANT_ARG_PASS);
}
void SceneTree::notify_group(const StringName &p_group, int p_notification) {
@@ -658,13 +662,15 @@ void SceneTree::_notification(int p_notification) {
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
if (!Engine::get_singleton()->is_editor_hint()) {
- get_root()->propagate_notification(Node::NOTIFICATION_TRANSLATION_CHANGED);
+ get_root()->propagate_notification(p_notification);
}
} break;
case NOTIFICATION_WM_UNFOCUS_REQUEST: {
notify_group_flags(GROUP_CALL_REALTIME | GROUP_CALL_MULTILEVEL, "input", NOTIFICATION_WM_UNFOCUS_REQUEST);
+ get_root()->propagate_notification(p_notification);
+
} break;
case NOTIFICATION_WM_ABOUT: {
@@ -1158,7 +1164,7 @@ void SceneTree::_update_root_rect() {
WARN_PRINT("Font oversampling only works with the resize modes 'Keep Width', 'Keep Height', and 'Expand'.");
}
- if (stretch_aspect == STRETCH_ASPECT_IGNORE || ABS(viewport_aspect - video_mode_aspect) < CMP_EPSILON) {
+ if (stretch_aspect == STRETCH_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) {
//same aspect or ignore aspect
viewport_size = desired_res;
screen_size = video_mode;
@@ -1894,6 +1900,7 @@ void SceneTree::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_changed"));
ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("node_renamed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("screen_resized"));
ADD_SIGNAL(MethodInfo("node_configuration_warning_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
@@ -1982,6 +1989,7 @@ SceneTree::SceneTree() {
tree_changed_name = "tree_changed";
node_added_name = "node_added";
node_removed_name = "node_removed";
+ node_renamed_name = "node_renamed";
ugc_locked = false;
call_lock = 0;
root_lock = 0;
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index e15a64604d..0bcb724929 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -126,6 +126,7 @@ private:
StringName tree_changed_name;
StringName node_added_name;
StringName node_removed_name;
+ StringName node_renamed_name;
bool use_font_oversampling;
int64_t current_frame;
@@ -201,6 +202,7 @@ private:
void tree_changed();
void node_added(Node *p_node);
void node_removed(Node *p_node);
+ void node_renamed(Node *p_node);
Group *add_to_group(const StringName &p_group, Node *p_node);
void remove_from_group(const StringName &p_group, Node *p_node);
@@ -287,7 +289,7 @@ protected:
public:
enum {
- NOTIFICATION_TRANSFORM_CHANGED = 29
+ NOTIFICATION_TRANSFORM_CHANGED = 2000
};
enum GroupCallFlags {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 4f1330ee36..c02873cdeb 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -232,16 +232,16 @@ void Viewport::update_worlds() {
find_world()->_update(get_tree()->get_frame());
}
-void Viewport::_collision_object_input_event(CollisionObject *p_object, Camera *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape, bool p_discard_empty_motion) {
+void Viewport::_collision_object_input_event(CollisionObject *p_object, Camera *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) {
Transform object_transform = p_object->get_global_transform();
Transform camera_transform = p_camera->get_global_transform();
ObjectID id = p_object->get_instance_id();
- if (p_discard_empty_motion) {
- //avoid sending the event unnecessarily if nothing really changed in the context
+ //avoid sending the fake event unnecessarily if nothing really changed in the context
+ if (object_transform == physics_last_object_transform && camera_transform == physics_last_camera_transform && physics_last_id == id) {
Ref<InputEventMouseMotion> mm = p_input_event;
- if (mm.is_valid() && object_transform == physics_last_object_transform && camera_transform == physics_last_camera_transform && physics_last_id == id) {
+ if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
return; //discarded
}
}
@@ -391,14 +391,14 @@ void Viewport::_notification(int p_what) {
if (physics_object_picking && (to_screen_rect == Rect2() || Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED)) {
+#ifndef _3D_DISABLED
Vector2 last_pos(1e20, 1e20);
CollisionObject *last_object = NULL;
ObjectID last_id = 0;
+#endif
PhysicsDirectSpaceState::RayResult result;
Physics2DDirectSpaceState *ss2d = Physics2DServer::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
- bool discard_empty_motion = false;
-
if (physics_has_last_mousepos) {
// if no mouse event exists, create a motion one. This is necessary because objects or camera may have moved.
// while this extra event is sent, it is checked if both camera and last object and last ID did not move. If nothing changed, the event is discarded to avoid flooding with unnecessary motion events every frame
@@ -414,6 +414,7 @@ void Viewport::_notification(int p_what) {
if (!has_mouse_event) {
Ref<InputEventMouseMotion> mm;
mm.instance();
+ 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);
@@ -422,7 +423,6 @@ void Viewport::_notification(int p_what) {
mm->set_metakey(physics_last_mouse_state.meta);
mm->set_button_mask(physics_last_mouse_state.mouse_mask);
physics_picking_events.push_back(mm);
- discard_empty_motion = true;
}
}
@@ -470,7 +470,7 @@ void Viewport::_notification(int p_what) {
physics_last_mouse_state.mouse_mask &= ~(1 << (mb->get_button_index() - 1));
// If touch mouse raised, assume we don't know last mouse pos until new events come
- if (mb->get_device() == -1) {
+ if (mb->get_device() == InputEvent::DEVICE_ID_TOUCH_MOUSE) {
physics_has_last_mousepos = false;
}
}
@@ -525,18 +525,25 @@ void Viewport::_notification(int p_what) {
if (res[i].collider_id && res[i].collider) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
if (co) {
-
+ bool send_event = true;
if (is_mouse) {
Map<ObjectID, uint64_t>::Element *F = physics_2d_mouseover.find(res[i].collider_id);
+
if (!F) {
F = physics_2d_mouseover.insert(res[i].collider_id, frame);
co->_mouse_enter();
} else {
F->get() = frame;
+ // It was already hovered, so don't send the event if it's faked
+ if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
+ send_event = false;
+ }
}
}
- co->_input_event(this, ev, res[i].shape);
+ if (send_event) {
+ co->_input_event(this, ev, res[i].shape);
+ }
}
}
}
@@ -573,7 +580,7 @@ void Viewport::_notification(int p_what) {
CollisionObject *co = Object::cast_to<CollisionObject>(ObjectDB::get_instance(physics_object_capture));
if (co) {
- _collision_object_input_event(co, camera, ev, Vector3(), Vector3(), 0, discard_empty_motion);
+ _collision_object_input_event(co, camera, ev, Vector3(), Vector3(), 0);
captured = true;
if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
physics_object_capture = 0;
@@ -591,7 +598,7 @@ void Viewport::_notification(int p_what) {
if (last_id) {
if (ObjectDB::get_instance(last_id) && last_object) {
//good, exists
- _collision_object_input_event(last_object, camera, ev, result.position, result.normal, result.shape, discard_empty_motion);
+ _collision_object_input_event(last_object, camera, ev, result.position, result.normal, result.shape);
if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
physics_object_capture = last_id;
}
@@ -614,7 +621,7 @@ void Viewport::_notification(int p_what) {
CollisionObject *co = Object::cast_to<CollisionObject>(result.collider);
if (co) {
- _collision_object_input_event(co, camera, ev, result.position, result.normal, result.shape, discard_empty_motion);
+ _collision_object_input_event(co, camera, ev, result.position, result.normal, result.shape);
last_object = co;
last_id = result.collider_id;
new_collider = last_id;
@@ -654,7 +661,11 @@ void Viewport::_notification(int p_what) {
}
} break;
+ case SceneTree::NOTIFICATION_WM_MOUSE_EXIT:
case SceneTree::NOTIFICATION_WM_FOCUS_OUT: {
+
+ _drop_physics_mouseover();
+
if (gui.mouse_focus) {
//if mouse is being pressed, send a release event
_drop_mouse_focus();
@@ -875,7 +886,7 @@ void Viewport::_camera_set(Camera *p_camera) {
if (camera == p_camera)
return;
- if (camera && find_world().is_valid()) {
+ if (camera) {
camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
}
camera = p_camera;
@@ -884,7 +895,7 @@ void Viewport::_camera_set(Camera *p_camera) {
else
VisualServer::get_singleton()->viewport_attach_camera(viewport, RID());
- if (camera && find_world().is_valid()) {
+ if (camera) {
camera->notification(Camera::NOTIFICATION_BECAME_CURRENT);
}
@@ -903,9 +914,7 @@ void Viewport::_camera_remove(Camera *p_camera) {
cameras.erase(p_camera);
if (camera == p_camera) {
- if (camera && find_world().is_valid()) {
- camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
- }
+ camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
camera = NULL;
}
}
@@ -1002,7 +1011,7 @@ void Viewport::_propagate_enter_world(Node *p_node) {
Viewport *v = Object::cast_to<Viewport>(p_node);
if (v) {
- if (v->world.is_valid())
+ if (v->world.is_valid() || v->own_world.is_valid())
return;
}
}
@@ -1039,7 +1048,7 @@ void Viewport::_propagate_exit_world(Node *p_node) {
Viewport *v = Object::cast_to<Viewport>(p_node);
if (v) {
- if (v->world.is_valid())
+ if (v->world.is_valid() || v->own_world.is_valid())
return;
}
}
@@ -1059,23 +1068,11 @@ void Viewport::set_world(const Ref<World> &p_world) {
if (is_inside_tree())
_propagate_exit_world(this);
-#ifndef _3D_DISABLED
- if (find_world().is_valid() && camera)
- camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
-#endif
-
world = p_world;
if (is_inside_tree())
_propagate_enter_world(this);
-#ifndef _3D_DISABLED
- if (find_world().is_valid() && camera)
- camera->notification(Camera::NOTIFICATION_BECAME_CURRENT);
-#endif
-
- //propagate exit
-
if (is_inside_tree()) {
VisualServer::get_singleton()->viewport_set_scenario(viewport, find_world()->get_scenario());
}
@@ -1471,7 +1468,8 @@ void Viewport::_gui_show_tooltip() {
gui.tooltip_popup->set_as_toplevel(true);
//gui.tooltip_popup->hide();
- Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_popup->get_minimum_size());
+ Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset");
+ Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_minimum_size());
Rect2 vr = gui.tooltip_popup->get_viewport_rect();
if (r.size.x + r.position.x > vr.size.x)
r.position.x = vr.size.x - r.size.x;
@@ -2290,32 +2288,32 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (from && p_event->is_pressed()) {
Control *next = NULL;
- if (p_event->is_action("ui_focus_next")) {
+ if (p_event->is_action_pressed("ui_focus_next")) {
next = from->find_next_valid_focus();
}
- if (p_event->is_action("ui_focus_prev")) {
+ if (p_event->is_action_pressed("ui_focus_prev")) {
next = from->find_prev_valid_focus();
}
- if (!mods && p_event->is_action("ui_up")) {
+ if (!mods && p_event->is_action_pressed("ui_up")) {
next = from->_get_focus_neighbour(MARGIN_TOP);
}
- if (!mods && p_event->is_action("ui_left")) {
+ if (!mods && p_event->is_action_pressed("ui_left")) {
next = from->_get_focus_neighbour(MARGIN_LEFT);
}
- if (!mods && p_event->is_action("ui_right")) {
+ if (!mods && p_event->is_action_pressed("ui_right")) {
next = from->_get_focus_neighbour(MARGIN_RIGHT);
}
- if (!mods && p_event->is_action("ui_down")) {
+ if (!mods && p_event->is_action_pressed("ui_down")) {
next = from->_get_focus_neighbour(MARGIN_BOTTOM);
}
@@ -2470,11 +2468,7 @@ void Viewport::_gui_hid_control(Control *p_control) {
if (gui.mouse_over == p_control)
gui.mouse_over = NULL;
if (gui.tooltip == p_control)
- gui.tooltip = NULL;
- if (gui.tooltip == p_control) {
- gui.tooltip = NULL;
_gui_cancel_tooltip();
- }
}
void Viewport::_gui_remove_control(Control *p_control) {
@@ -2555,6 +2549,31 @@ void Viewport::_drop_mouse_focus() {
}
}
+void Viewport::_drop_physics_mouseover() {
+
+ physics_has_last_mousepos = false;
+
+ while (physics_2d_mouseover.size()) {
+ Object *o = ObjectDB::get_instance(physics_2d_mouseover.front()->key());
+ if (o) {
+ CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
+ co->_mouse_exit();
+ }
+ physics_2d_mouseover.erase(physics_2d_mouseover.front());
+ }
+
+#ifndef _3D_DISABLED
+ if (physics_object_over) {
+ CollisionObject *co = Object::cast_to<CollisionObject>(ObjectDB::get_instance(physics_object_over));
+ if (co) {
+ co->_mouse_exit();
+ }
+ }
+
+ physics_object_over = physics_object_capture = 0;
+#endif
+}
+
List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
gui.modal_stack.push_back(p_control);
@@ -2689,11 +2708,6 @@ void Viewport::set_use_own_world(bool p_world) {
if (is_inside_tree())
_propagate_exit_world(this);
-#ifndef _3D_DISABLED
- if (find_world().is_valid() && camera)
- camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
-#endif
-
if (!p_world)
own_world = Ref<World>();
else
@@ -2702,13 +2716,6 @@ void Viewport::set_use_own_world(bool p_world) {
if (is_inside_tree())
_propagate_enter_world(this);
-#ifndef _3D_DISABLED
- if (find_world().is_valid() && camera)
- camera->notification(Camera::NOTIFICATION_BECAME_CURRENT);
-#endif
-
- //propagate exit
-
if (is_inside_tree()) {
VisualServer::get_singleton()->viewport_set_scenario(viewport, find_world()->get_scenario());
}
@@ -2732,6 +2739,19 @@ Rect2 Viewport::get_attach_to_screen_rect() const {
return to_screen_rect;
}
+void Viewport::set_use_render_direct_to_screen(bool p_render_direct_to_screen) {
+
+ if (p_render_direct_to_screen == render_direct_to_screen)
+ return;
+
+ render_direct_to_screen = p_render_direct_to_screen;
+ VS::get_singleton()->viewport_set_render_direct_to_screen(viewport, p_render_direct_to_screen);
+}
+
+bool Viewport::is_using_render_direct_to_screen() const {
+ return render_direct_to_screen;
+}
+
void Viewport::set_physics_object_picking(bool p_enable) {
physics_object_picking = p_enable;
@@ -2997,6 +3017,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
ClassDB::bind_method(D_METHOD("set_attach_to_screen_rect", "rect"), &Viewport::set_attach_to_screen_rect);
+ ClassDB::bind_method(D_METHOD("set_use_render_direct_to_screen", "enable"), &Viewport::set_use_render_direct_to_screen);
+ ClassDB::bind_method(D_METHOD("is_using_render_direct_to_screen"), &Viewport::is_using_render_direct_to_screen);
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse);
@@ -3051,6 +3073,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_3d_linear"), "set_keep_3d_linear", "get_keep_3d_linear");
ADD_PROPERTY(PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_ENUM, "2D,2D No-Sampling,3D,3D No-Effects"), "set_usage", "get_usage");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_direct_to_screen"), "set_use_render_direct_to_screen", "is_using_render_direct_to_screen");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Render Target", "render_target_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_target_v_flip"), "set_vflip", "get_vflip");
@@ -3133,6 +3156,8 @@ Viewport::Viewport() {
texture_rid = VisualServer::get_singleton()->viewport_get_texture(viewport);
texture_flags = 0;
+ render_direct_to_screen = false;
+
default_texture.instance();
default_texture->vp = const_cast<Viewport *>(this);
viewport_textures.insert(default_texture.ptr());
@@ -3187,7 +3212,7 @@ Viewport::Viewport() {
gui.tooltip_timer = -1;
//gui.tooltip_timer->force_parent_owned();
- gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.7);
+ gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.5);
ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::REAL, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers
gui.tooltip = NULL;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 28a52ac4b6..b7160d5139 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -182,6 +182,7 @@ private:
Size2 size;
Rect2 to_screen_rect;
+ bool render_direct_to_screen;
RID contact_2d_debug;
RID contact_3d_debug_multimesh;
@@ -221,7 +222,7 @@ private:
} physics_last_mouse_state;
- void _collision_object_input_event(CollisionObject *p_object, Camera *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape, bool p_discard_empty_motion);
+ void _collision_object_input_event(CollisionObject *p_object, Camera *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape);
bool handle_input_locally;
bool local_input_handled;
@@ -383,6 +384,7 @@ private:
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
void _drop_mouse_focus();
+ void _drop_physics_mouseover();
void _update_canvas_items(Node *p_node);
@@ -480,6 +482,9 @@ public:
void set_attach_to_screen_rect(const Rect2 &p_rect);
Rect2 get_attach_to_screen_rect() const;
+ void set_use_render_direct_to_screen(bool p_render_direct_to_screen);
+ bool is_using_render_direct_to_screen() const;
+
Vector2 get_mouse_position() const;
void warp_mouse(const Vector2 &p_pos);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 49c9c4c23c..8e6e8e2a53 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -139,11 +139,11 @@
#include "scene/resources/dynamic_font.h"
#include "scene/resources/dynamic_font_stb.h"
#include "scene/resources/gradient.h"
+#include "scene/resources/height_map_shape.h"
#include "scene/resources/line_shape_2d.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/mesh_data_tool.h"
-#include "scene/resources/mesh_library.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/particles_material.h"
#include "scene/resources/physics_material.h"
@@ -208,6 +208,7 @@
#include "scene/3d/visibility_notifier.h"
#include "scene/animation/skeleton_ik.h"
#include "scene/resources/environment.h"
+#include "scene/resources/mesh_library.h"
#endif
static Ref<ResourceFormatSaverText> resource_saver_text;
@@ -467,7 +468,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
#endif
- ClassDB::register_class<MeshLibrary>();
+
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
ClassDB::register_class<Shader>();
@@ -475,7 +476,9 @@ void register_scene_types() {
ClassDB::register_virtual_class<VisualShaderNode>();
ClassDB::register_class<VisualShaderNodeInput>();
ClassDB::register_virtual_class<VisualShaderNodeOutput>();
+ ClassDB::register_class<VisualShaderNodeGroupBase>();
ClassDB::register_class<VisualShaderNodeScalarConstant>();
+ ClassDB::register_class<VisualShaderNodeBooleanConstant>();
ClassDB::register_class<VisualShaderNodeColorConstant>();
ClassDB::register_class<VisualShaderNodeVec3Constant>();
ClassDB::register_class<VisualShaderNodeTransformConstant>();
@@ -486,8 +489,23 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeTransformVecMult>();
ClassDB::register_class<VisualShaderNodeScalarFunc>();
ClassDB::register_class<VisualShaderNodeVectorFunc>();
+ ClassDB::register_class<VisualShaderNodeColorFunc>();
+ ClassDB::register_class<VisualShaderNodeTransformFunc>();
ClassDB::register_class<VisualShaderNodeDotProduct>();
ClassDB::register_class<VisualShaderNodeVectorLen>();
+ ClassDB::register_class<VisualShaderNodeDeterminant>();
+ ClassDB::register_class<VisualShaderNodeScalarDerivativeFunc>();
+ ClassDB::register_class<VisualShaderNodeVectorDerivativeFunc>();
+ ClassDB::register_class<VisualShaderNodeScalarClamp>();
+ ClassDB::register_class<VisualShaderNodeVectorClamp>();
+ ClassDB::register_class<VisualShaderNodeFaceForward>();
+ ClassDB::register_class<VisualShaderNodeOuterProduct>();
+ ClassDB::register_class<VisualShaderNodeVectorScalarStep>();
+ ClassDB::register_class<VisualShaderNodeScalarSmoothStep>();
+ ClassDB::register_class<VisualShaderNodeVectorSmoothStep>();
+ ClassDB::register_class<VisualShaderNodeVectorScalarSmoothStep>();
+ ClassDB::register_class<VisualShaderNodeVectorDistance>();
+ ClassDB::register_class<VisualShaderNodeVectorRefract>();
ClassDB::register_class<VisualShaderNodeScalarInterp>();
ClassDB::register_class<VisualShaderNodeVectorInterp>();
ClassDB::register_class<VisualShaderNodeVectorCompose>();
@@ -498,11 +516,16 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeCubeMap>();
ClassDB::register_virtual_class<VisualShaderNodeUniform>();
ClassDB::register_class<VisualShaderNodeScalarUniform>();
+ ClassDB::register_class<VisualShaderNodeBooleanUniform>();
ClassDB::register_class<VisualShaderNodeColorUniform>();
ClassDB::register_class<VisualShaderNodeVec3Uniform>();
ClassDB::register_class<VisualShaderNodeTransformUniform>();
ClassDB::register_class<VisualShaderNodeTextureUniform>();
ClassDB::register_class<VisualShaderNodeCubeMapUniform>();
+ ClassDB::register_class<VisualShaderNodeIf>();
+ ClassDB::register_class<VisualShaderNodeSwitch>();
+ ClassDB::register_class<VisualShaderNodeFresnel>();
+ ClassDB::register_class<VisualShaderNodeExpression>();
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
@@ -591,6 +614,7 @@ void register_scene_types() {
ClassDB::register_class<BoxShape>();
ClassDB::register_class<CapsuleShape>();
ClassDB::register_class<CylinderShape>();
+ ClassDB::register_class<HeightMapShape>();
ClassDB::register_class<PlaneShape>();
ClassDB::register_class<ConvexPolygonShape>();
ClassDB::register_class<ConcavePolygonShape>();
@@ -614,6 +638,7 @@ void register_scene_types() {
ClassDB::register_class<StreamTexture>();
ClassDB::register_class<ImageTexture>();
ClassDB::register_class<AtlasTexture>();
+ ClassDB::register_class<MeshTexture>();
ClassDB::register_class<LargeTexture>();
ClassDB::register_class<CurveTexture>();
ClassDB::register_class<GradientTexture>();
@@ -760,7 +785,11 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_bmfont);
resource_loader_bmfont.unref();
+ //SpatialMaterial is not initialised when 3D is disabled, so it shouldn't be cleaned up either
+#ifndef _3D_DISABLED
SpatialMaterial::finish_shaders();
+#endif // _3D_DISABLED
+
ParticlesMaterial::finish_shaders();
CanvasItemMaterial::finish_shaders();
SceneStringNames::free();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 3eb16c544c..b49b551252 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -819,15 +819,17 @@ int Animation::_insert(float p_time, T &p_keys, const V &p_value) {
while (true) {
- if (idx == 0 || p_keys[idx - 1].time < p_time) {
- //condition for insertion.
- p_keys.insert(idx, p_value);
- return idx;
- } else if (p_keys[idx - 1].time == p_time) {
+ // Condition for replacement.
+ if (idx > 0 && Math::is_equal_approx(p_keys[idx - 1].time, p_time)) {
- // condition for replacing.
p_keys.write[idx - 1] = p_value;
return idx - 1;
+
+ // Condition for insert.
+ } else if (idx == 0 || p_keys[idx - 1].time < p_time) {
+
+ p_keys.insert(idx, p_value);
+ return idx;
}
idx--;
@@ -1296,6 +1298,78 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_V(-1);
}
+void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+
+ switch (t->type) {
+
+ case TYPE_TRANSFORM: {
+
+ TransformTrack *tt = static_cast<TransformTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+ TKey<TransformKey> key = tt->transforms[p_key_idx];
+ key.time = p_time;
+ tt->transforms.remove(p_key_idx);
+ _insert(p_time, tt->transforms, key);
+ return;
+ }
+ case TYPE_VALUE: {
+
+ ValueTrack *vt = static_cast<ValueTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, vt->values.size());
+ TKey<Variant> key = vt->values[p_key_idx];
+ key.time = p_time;
+ vt->values.remove(p_key_idx);
+ _insert(p_time, vt->values, key);
+ return;
+ }
+ case TYPE_METHOD: {
+
+ MethodTrack *mt = static_cast<MethodTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, mt->methods.size());
+ MethodKey key = mt->methods[p_key_idx];
+ key.time = p_time;
+ mt->methods.remove(p_key_idx);
+ _insert(p_time, mt->methods, key);
+ return;
+ }
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, bt->values.size());
+ TKey<BezierKey> key = bt->values[p_key_idx];
+ key.time = p_time;
+ bt->values.remove(p_key_idx);
+ _insert(p_time, bt->values, key);
+ return;
+ }
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, at->values.size());
+ TKey<AudioKey> key = at->values[p_key_idx];
+ key.time = p_time;
+ at->values.remove(p_key_idx);
+ _insert(p_time, at->values, key);
+ return;
+ }
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, at->values.size());
+ TKey<StringName> key = at->values[p_key_idx];
+ key.time = p_time;
+ at->values.remove(p_key_idx);
+ _insert(p_time, at->values, key);
+ return;
+ }
+ }
+
+ ERR_FAIL();
+}
+
float Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
@@ -1466,7 +1540,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
int high = len - 1;
int middle = 0;
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
if (low > high)
ERR_PRINT("low > high, this may be a bug");
#endif
@@ -1477,7 +1551,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
middle = (low + high) / 2;
- if (Math::abs(p_time - keys[middle].time) < CMP_EPSILON) { //match
+ if (Math::is_equal_approx(p_time, keys[middle].time)) { //match
return middle;
} else if (p_time < keys[middle].time)
high = middle - 1; //search low end of array
@@ -1680,10 +1754,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = p_keys[next].time - p_keys[idx].time;
float from = p_time - p_keys[idx].time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
} else {
@@ -1691,10 +1765,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = (length - p_keys[idx].time) + p_keys[next].time;
float from = p_time - p_keys[idx].time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
}
} else {
@@ -1707,10 +1781,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = endtime + p_keys[next].time;
float from = endtime + p_time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
}
} else { // no loop
@@ -1723,10 +1797,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = p_keys[next].time - p_keys[idx].time;
float from = p_time - p_keys[idx].time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
} else {
@@ -2559,17 +2633,6 @@ bool Animation::has_loop() const {
return loop;
}
-void Animation::track_move_up(int p_track) {
-
- if (p_track >= 0 && p_track < (tracks.size() - 1)) {
-
- SWAP(tracks.write[p_track], tracks.write[p_track + 1]);
- }
-
- emit_changed();
- emit_signal(SceneStringNames::get_singleton()->tracks_changed);
-}
-
void Animation::track_set_imported(int p_track, bool p_imported) {
ERR_FAIL_INDEX(p_track, tracks.size());
@@ -2595,12 +2658,40 @@ bool Animation::track_is_enabled(int p_track) const {
return tracks[p_track]->enabled;
}
+void Animation::track_move_up(int p_track) {
+
+ if (p_track >= 0 && p_track < (tracks.size() - 1)) {
+
+ SWAP(tracks.write[p_track], tracks.write[p_track + 1]);
+ }
+
+ emit_changed();
+ emit_signal(SceneStringNames::get_singleton()->tracks_changed);
+}
+
void Animation::track_move_down(int p_track) {
if (p_track > 0 && p_track < tracks.size()) {
SWAP(tracks.write[p_track], tracks.write[p_track - 1]);
}
+
+ emit_changed();
+ emit_signal(SceneStringNames::get_singleton()->tracks_changed);
+}
+
+void Animation::track_move_to(int p_track, int p_to_index) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ ERR_FAIL_INDEX(p_to_index, tracks.size() + 1);
+ if (p_track == p_to_index || p_track == p_to_index - 1)
+ return;
+
+ Track *track = tracks.get(p_track);
+ tracks.remove(p_track);
+ // Take into account that the position of the tracks that come after the one removed will change.
+ tracks.insert(p_to_index > p_track ? p_to_index - 1 : p_to_index, track);
+
emit_changed();
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
@@ -2612,6 +2703,7 @@ void Animation::track_swap(int p_track, int p_with_track) {
if (p_track == p_with_track)
return;
SWAP(tracks.write[p_track], tracks.write[p_with_track]);
+
emit_changed();
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
@@ -2638,6 +2730,7 @@ void Animation::copy_track(int p_track, Ref<Animation> p_to_animation) {
p_to_animation->track_set_enabled(dst_track, track_is_enabled(p_track));
p_to_animation->track_set_interpolation_type(dst_track, track_get_interpolation_type(p_track));
p_to_animation->track_set_interpolation_loop_wrap(dst_track, track_get_interpolation_loop_wrap(p_track));
+ p_to_animation->value_track_set_update_mode(dst_track, value_track_get_update_mode(p_track));
for (int i = 0; i < track_get_key_count(p_track); i++) {
p_to_animation->track_insert_key(dst_track, track_get_key_time(p_track, i), track_get_key_value(p_track, i), track_get_key_transition(p_track, i));
}
@@ -2655,6 +2748,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_move_up", "idx"), &Animation::track_move_up);
ClassDB::bind_method(D_METHOD("track_move_down", "idx"), &Animation::track_move_down);
+ ClassDB::bind_method(D_METHOD("track_move_to", "idx", "to_idx"), &Animation::track_move_to);
ClassDB::bind_method(D_METHOD("track_swap", "idx", "with_idx"), &Animation::track_swap);
ClassDB::bind_method(D_METHOD("track_set_imported", "idx", "imported"), &Animation::track_set_imported);
@@ -2669,6 +2763,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "idx", "position"), &Animation::track_remove_key_at_position);
ClassDB::bind_method(D_METHOD("track_set_key_value", "idx", "key", "value"), &Animation::track_set_key_value);
ClassDB::bind_method(D_METHOD("track_set_key_transition", "idx", "key_idx", "transition"), &Animation::track_set_key_transition);
+ ClassDB::bind_method(D_METHOD("track_set_key_time", "idx", "key_idx", "time"), &Animation::track_set_key_time);
ClassDB::bind_method(D_METHOD("track_get_key_transition", "idx", "key_idx"), &Animation::track_get_key_transition);
ClassDB::bind_method(D_METHOD("track_get_key_count", "idx"), &Animation::track_get_key_count);
@@ -2773,9 +2868,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
const Vector3 &v1 = t1.value.loc;
const Vector3 &v2 = t2.value.loc;
- if (v0.distance_to(v2) < CMP_EPSILON) {
+ if (Math::is_zero_approx(v0.distance_to(v2))) {
//0 and 2 are close, let's see if 1 is close
- if (v0.distance_to(v1) > CMP_EPSILON) {
+ if (!Math::is_zero_approx(v0.distance_to(v1))) {
//not close, not optimizable
return false;
}
@@ -2812,9 +2907,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
//localize both to rotation from q0
- if ((q0 - q2).length() < CMP_EPSILON) {
+ if (Math::is_zero_approx((q0 - q2).length())) {
- if ((q0 - q1).length() > CMP_EPSILON)
+ if (!Math::is_zero_approx((q0 - q1).length()))
return false;
} else {
@@ -2862,9 +2957,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
const Vector3 &v1 = t1.value.scale;
const Vector3 &v2 = t2.value.scale;
- if (v0.distance_to(v2) < CMP_EPSILON) {
+ if (Math::is_zero_approx(v0.distance_to(v2))) {
//0 and 2 are close, let's see if 1 is close
- if (v0.distance_to(v1) > CMP_EPSILON) {
+ if (!Math::is_zero_approx(v0.distance_to(v1))) {
//not close, not optimizable
return false;
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index b66ae184e9..59f2ae24c7 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -93,7 +93,6 @@ private:
template <class T>
struct TKey : public Key {
- float time;
T value;
};
@@ -294,6 +293,7 @@ public:
void track_move_up(int p_track);
void track_move_down(int p_track);
+ void track_move_to(int p_track, int p_to_index);
void track_swap(int p_track, int p_with_track);
void track_set_imported(int p_track, bool p_imported);
@@ -305,6 +305,7 @@ public:
void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1);
void track_set_key_transition(int p_track, int p_key_idx, float p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
+ void track_set_key_time(int p_track, int p_key_idx, float p_time);
int track_find_key(int p_track, float p_time, bool p_exact = false) const;
void track_remove_key(int p_track, int p_idx);
void track_remove_key_at_position(int p_track, float p_pos);
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index a89cf108bc..4b3e392013 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -515,10 +515,10 @@ PoolVector<uint8_t> AudioStreamSample::get_data() const {
return pv;
}
-void AudioStreamSample::save_to_wav(String p_path) {
+Error AudioStreamSample::save_to_wav(const String &p_path) {
if (format == AudioStreamSample::FORMAT_IMA_ADPCM) {
WARN_PRINTS("Saving IMA_ADPC samples are not supported yet");
- return;
+ return ERR_UNAVAILABLE;
}
int sub_chunk_2_size = data_bytes; //Subchunk2Size = Size of data in bytes
@@ -544,8 +544,9 @@ void AudioStreamSample::save_to_wav(String p_path) {
file_path += ".wav";
}
- Error err;
- FileAccess *file = FileAccess::open(file_path, FileAccess::WRITE, &err); //Overrides existing file if present
+ FileAccessRef file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present
+
+ ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
// Create WAV Header
file->store_string("RIFF"); //ChunkID
@@ -583,6 +584,8 @@ void AudioStreamSample::save_to_wav(String p_path) {
}
file->close();
+
+ return OK;
}
Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() {
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index bd701ddd12..d4c5511f34 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -141,7 +141,7 @@ public:
void set_data(const PoolVector<uint8_t> &p_data);
PoolVector<uint8_t> get_data() const;
- void save_to_wav(String p_path);
+ Error save_to_wav(const String &p_path);
virtual Ref<AudioStreamPlayback> instance_playback();
virtual String get_stream_name() const;
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index d2e4d28b44..e4a64a1de1 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -96,7 +96,7 @@ int BitMap::get_true_bit_count() const {
const uint8_t *d = bitmask.ptr();
int c = 0;
- //fast, almot branchless version
+ //fast, almost branchless version
for (int i = 0; i < ds; i++) {
@@ -106,6 +106,7 @@ int BitMap::get_true_bit_count() const {
c += (d[i] & (1 << 4)) >> 4;
c += (d[i] & (1 << 3)) >> 3;
c += (d[i] & (1 << 2)) >> 2;
+ c += (d[i] & (1 << 1)) >> 1;
c += d[i] & 1;
}
@@ -594,6 +595,62 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con
return result_array;
}
+void BitMap::resize(const Size2 &p_new_size) {
+
+ Ref<BitMap> new_bitmap;
+ new_bitmap.instance();
+ new_bitmap->create(p_new_size);
+ int lw = MIN(width, p_new_size.width);
+ int lh = MIN(height, p_new_size.height);
+ for (int x = 0; x < lw; x++) {
+ for (int y = 0; y < lh; y++) {
+ new_bitmap->set_bit(Vector2(x, y), get_bit(Vector2(x, y)));
+ }
+ }
+
+ width = new_bitmap->width;
+ height = new_bitmap->height;
+ bitmask = new_bitmap->bitmask;
+}
+
+Ref<Image> BitMap::convert_to_image() const {
+
+ Ref<Image> image;
+ image.instance();
+ image->create(width, height, false, Image::FORMAT_L8);
+ image->lock();
+ for (int i = 0; i < width; i++) {
+ for (int j = 0; j < height; j++) {
+ image->set_pixel(i, j, get_bit(Point2(i, j)) ? Color(1, 1, 1) : Color(0, 0, 0));
+ }
+ }
+
+ image->unlock();
+
+ return image;
+}
+void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) {
+
+ int x = p_pos.x;
+ int y = p_pos.y;
+ int w = p_bitmap->get_size().width;
+ int h = p_bitmap->get_size().height;
+
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ int px = x + i;
+ int py = y + j;
+ if (px < 0 || px >= width)
+ continue;
+ if (py < 0 || py >= height)
+ continue;
+ if (p_bitmap->get_bit(Vector2(i, j))) {
+ set_bit(Vector2(x, y), true);
+ }
+ }
+ }
+}
+
void BitMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create);
diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h
index b3c86afd38..daf24affb1 100644
--- a/scene/resources/bit_map.h
+++ b/scene/resources/bit_map.h
@@ -64,9 +64,13 @@ public:
int get_true_bit_count() const;
Size2 get_size() const;
+ void resize(const Size2 &p_new_size);
void grow_mask(int p_pixels, const Rect2 &p_rect);
+ void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap);
+ Ref<Image> convert_to_image() const;
+
Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const;
BitMap();
diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp
index d93754076c..d819e9f776 100644
--- a/scene/resources/box_shape.cpp
+++ b/scene/resources/box_shape.cpp
@@ -51,6 +51,7 @@ Vector<Vector3> BoxShape::_gen_debug_mesh_lines() {
void BoxShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), extents);
+ Shape::_update_shape();
}
void BoxShape::set_extents(const Vector3 &p_extents) {
diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp
index 3f7bf1e0ec..669b261bfe 100644
--- a/scene/resources/capsule_shape.cpp
+++ b/scene/resources/capsule_shape.cpp
@@ -75,6 +75,7 @@ void CapsuleShape::_update_shape() {
d["radius"] = radius;
d["height"] = height;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+ Shape::_update_shape();
}
void CapsuleShape::set_radius(float p_radius) {
diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp
index b192d088d8..b4cc38c8c0 100644
--- a/scene/resources/concave_polygon_shape.cpp
+++ b/scene/resources/concave_polygon_shape.cpp
@@ -65,6 +65,7 @@ Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() {
}
void ConcavePolygonShape::_update_shape() {
+ Shape::_update_shape();
}
void ConcavePolygonShape::set_faces(const PoolVector<Vector3> &p_faces) {
diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp
index 98d3460ed2..499688a185 100644
--- a/scene/resources/convex_polygon_shape.cpp
+++ b/scene/resources/convex_polygon_shape.cpp
@@ -58,7 +58,7 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() {
void ConvexPolygonShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), points);
- emit_changed();
+ Shape::_update_shape();
}
void ConvexPolygonShape::set_points(const PoolVector<Vector3> &p_points) {
@@ -83,6 +83,4 @@ void ConvexPolygonShape::_bind_methods() {
ConvexPolygonShape::ConvexPolygonShape() :
Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON)) {
-
- //set_points(Vector3(1,1,1));
}
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index f275405de2..d424fb2814 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -99,10 +99,4 @@ Rect2 ConvexPolygonShape2D::get_rect() const {
ConvexPolygonShape2D::ConvexPolygonShape2D() :
Shape2D(Physics2DServer::get_singleton()->convex_polygon_shape_create()) {
-
- int pcount = 3;
- for (int i = 0; i < pcount; i++)
- points.push_back(Vector2(Math::sin(i * Math_PI * 2 / pcount), -Math::cos(i * Math_PI * 2 / pcount)) * 10);
-
- _update_shape();
}
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 464ca60d31..950518aa6e 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -268,7 +268,7 @@ void Curve::update_auto_tangents(int i) {
}
if (i + 1 < _points.size()) {
- if (p.right_mode == TANGENT_LINEAR && i + 1 < _points.size()) {
+ if (p.right_mode == TANGENT_LINEAR) {
Vector2 v = (_points[i + 1].pos - p.pos).normalized();
p.right_tangent = v.y / v.x;
}
@@ -782,7 +782,8 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
if (idx >= bpc - 1) {
return r[bpc - 1];
} else if (idx == bpc - 2) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ if (frac > 0)
+ frac /= Math::fmod(baked_max_ofs, bake_interval);
} else {
frac /= bake_interval;
}
@@ -1352,7 +1353,8 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
if (idx >= bpc - 1) {
return r[bpc - 1];
} else if (idx == bpc - 2) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ if (frac > 0)
+ frac /= Math::fmod(baked_max_ofs, bake_interval);
} else {
frac /= bake_interval;
}
@@ -1396,7 +1398,8 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const {
if (idx >= bpc - 1) {
return r[bpc - 1];
} else if (idx == bpc - 2) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ if (frac > 0)
+ frac /= Math::fmod(baked_max_ofs, bake_interval);
} else {
frac /= bake_interval;
}
diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp
index 4fd829b349..f60f7ab376 100644
--- a/scene/resources/cylinder_shape.cpp
+++ b/scene/resources/cylinder_shape.cpp
@@ -68,6 +68,7 @@ void CylinderShape::_update_shape() {
d["radius"] = radius;
d["height"] = height;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+ Shape::_update_shape();
}
void CylinderShape::set_radius(float p_radius) {
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index c7a815d8a4..bdb6c78782 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -359,7 +359,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "CheckButton", focus);
theme->set_icon("on", "CheckButton", make_icon(toggle_on_png));
+ theme->set_icon("on_disabled", "CheckButton", make_icon(toggle_on_disabled_png));
theme->set_icon("off", "CheckButton", make_icon(toggle_off_png));
+ theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png));
theme->set_font("font", "CheckButton", default_font);
@@ -423,6 +425,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
theme->set_icon("tab", "TextEdit", make_icon(tab_png));
+ theme->set_icon("space", "TextEdit", make_icon(space_png));
+ theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png));
+ theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png));
theme->set_font("font", "TextEdit", default_font);
@@ -436,7 +441,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color_selected", "TextEdit", Color(0, 0, 0));
theme->set_color("selection_color", "TextEdit", font_color_selection);
theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4));
+ theme->set_color("bookmark_color", "TextEdit", Color(0.08, 0.49, 0.98));
theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2));
+ theme->set_color("executing_line_color", "TextEdit", Color(0.2, 0.8, 0.2, 0.4));
theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8));
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
theme->set_color("caret_color", "TextEdit", control_font_color);
@@ -795,7 +802,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("bold_italics_font", "RichTextLabel", default_font);
theme->set_font("mono_font", "RichTextLabel", default_font);
- theme->set_color("default_color", "RichTextLabel", control_font_color);
+ theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1));
theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
@@ -830,14 +837,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("autohide", "HSplitContainer", 1 * scale);
theme->set_constant("autohide", "VSplitContainer", 1 * scale);
- // ReferenceRect
-
- Ref<StyleBoxTexture> ttnc = make_stylebox(full_panel_bg_png, 8, 8, 8, 8);
- ttnc->set_draw_center(false);
-
- theme->set_stylebox("panelnc", "Panel", ttnc);
- theme->set_stylebox("panelf", "Panel", tc_sb);
-
Ref<StyleBoxTexture> sb_pc = make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 7, 7, 7, 7);
theme->set_stylebox("panel", "PanelContainer", sb_pc);
diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py
index 73b1ae0b0b..bd5a723b23 100755
--- a/scene/resources/default_theme/make_header.py
+++ b/scene/resources/default_theme/make_header.py
@@ -1,8 +1,6 @@
#!/usr/bin/env python
-import os
import glob
-import string
enc = "utf-8"
diff --git a/scene/resources/default_theme/space.png b/scene/resources/default_theme/space.png
new file mode 100644
index 0000000000..7e458a6c45
--- /dev/null
+++ b/scene/resources/default_theme/space.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 2b251e5f81..87b8afd6a3 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -358,6 +358,10 @@ static const unsigned char selection_oof_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xaf, 0xdf, 0x90, 0xa5, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x2f, 0x33, 0x4, 0x32, 0xde, 0xce, 0x64, 0xf4, 0x68, 0x53, 0x0, 0x32, 0xfe, 0xcd, 0xa0, 0x90, 0x1, 0x37, 0x10, 0x6e, 0x5, 0xdc, 0x52, 0xb8, 0x33, 0x0, 0xcc, 0x7, 0x26, 0xff, 0x1f, 0x38, 0x23, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char space_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc4, 0xf, 0xbe, 0x8b, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x63, 0x60, 0xa0, 0x2a, 0xf8, 0xff, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xff, 0x5, 0x91, 0xc5, 0x58, 0x90, 0x24, 0x85, 0x18, 0x18, 0x18, 0x14, 0xa0, 0x6c, 0x6, 0x46, 0x46, 0xc6, 0xf7, 0xc, 0xc, 0xc, 0xc, 0x4c, 0x14, 0x5b, 0x41, 0x39, 0x0, 0x0, 0x71, 0x1a, 0x13, 0x5d, 0x87, 0xfe, 0x9e, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char spinbox_updown_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0xdc, 0x4f, 0x7f, 0x98, 0x86, 0x47, 0xfa, 0x81, 0xe5, 0x83, 0x1f, 0xf, 0x7e, 0x3d, 0xb2, 0xc5, 0xa5, 0x5b, 0xe2, 0xc1, 0x93, 0x7, 0xff, 0x81, 0xf0, 0xf9, 0x63, 0x69, 0x2c, 0xd2, 0x67, 0x58, 0xef, 0x1f, 0x2, 0x4a, 0x42, 0xe0, 0xf1, 0xdb, 0xec, 0x98, 0xfa, 0x67, 0x2, 0x25, 0xe0, 0xf0, 0xe1, 0x2, 0x86, 0x41, 0x7, 0x30, 0x1d, 0x39, 0x3, 0xbf, 0x37, 0x8f, 0xdd, 0x66, 0x27, 0x29, 0xa0, 0x10, 0x4a, 0x2c, 0xa0, 0x41, 0x8d, 0x1b, 0x3c, 0x4c, 0x3, 0x46, 0x16, 0x69, 0x0, 0x0, 0x87, 0x2a, 0x58, 0xb5, 0x18, 0xe9, 0x80, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -402,10 +406,18 @@ static const unsigned char toggle_off_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x7a, 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, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x2d, 0x26, 0x2c, 0x4d, 0x2b, 0x37, 0x63, 0x2f, 0x3f, 0x6e, 0x31, 0x43, 0x71, 0x32, 0x44, 0x6c, 0x31, 0x42, 0x51, 0x2c, 0x39, 0x47, 0x2a, 0x35, 0x66, 0x30, 0x40, 0x4d, 0x2b, 0x38, 0x32, 0x26, 0x2e, 0x26, 0x25, 0x2a, 0x2e, 0x25, 0x2c, 0x3c, 0x28, 0x31, 0x52, 0x2c, 0x39, 0x68, 0x30, 0x40, 0x27, 0x25, 0x2a, 0x50, 0x2c, 0x38, 0x5f, 0x2e, 0x3d, 0x35, 0x27, 0x2f, 0x38, 0x27, 0x30, 0x5e, 0x2e, 0x3d, 0x43, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x44, 0x2a, 0x34, 0x2b, 0x26, 0x2c, 0x64, 0x2f, 0x3f, 0x36, 0x27, 0x30, 0x37, 0x27, 0x30, 0x66, 0x2f, 0x40, 0x2c, 0x26, 0x2c, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x97, 0xb0, 0x86, 0xb4, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0x8, 0x17, 0x35, 0x86, 0xf3, 0x7, 0x3a, 0xb4, 0xb9, 0xb, 0x28, 0x8a, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xe6, 0x37, 0xf, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xe5, 0x7c, 0xf3, 0x66, 0x0, 0x0, 0x2, 0x29, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x3, 0x93, 0x24, 0x4b, 0x14, 0x85, 0x5f, 0xb9, 0xaa, 0x6d, 0x8e, 0x6d, 0xdb, 0x73, 0x2b, 0xb3, 0xe7, 0xad, 0x6d, 0xab, 0xdd, 0x63, 0xe3, 0xbf, 0x6f, 0x67, 0x65, 0xbb, 0xb4, 0xbb, 0x81, 0xc5, 0x97, 0x11, 0x27, 0x78, 0x4f, 0xea, 0xe2, 0xbf, 0x3f, 0x9, 0x86, 0xe5, 0x38, 0xde, 0x16, 0x8e, 0x63, 0x19, 0xc3, 0x70, 0x96, 0x17, 0x44, 0x49, 0x56, 0x1c, 0x36, 0x28, 0xb2, 0x24, 0xa, 0x3c, 0xab, 0xdf, 0x9d, 0x77, 0xca, 0x2e, 0xb7, 0xc7, 0xeb, 0xf3, 0x7, 0x2c, 0xf1, 0xfb, 0xbc, 0x1e, 0xb7, 0x4b, 0x76, 0xf2, 0x2d, 0xa7, 0x60, 0x83, 0xa1, 0x70, 0x24, 0x1a, 0x8b, 0x27, 0xb6, 0xc1, 0x86, 0xed, 0x44, 0x3c, 0x16, 0x8d, 0x84, 0x43, 0x41, 0xb6, 0x29, 0x3e, 0xd9, 0xd6, 0xde, 0xd1, 0xa9, 0x22, 0x84, 0x31, 0x6, 0xd, 0x4c, 0x40, 0x38, 0x85, 0xd, 0x4d, 0x3a, 0x3b, 0xda, 0xdb, 0x92, 0xd, 0xe, 0x4c, 0x97, 0xd2, 0xdd, 0xa3, 0xa2, 0x9d, 0xff, 0x6f, 0xdd, 0xbe, 0x43, 0xb9, 0x7b, 0x6f, 0x47, 0x73, 0x49, 0x21, 0x93, 0x73, 0xf4, 0x74, 0x2b, 0x5d, 0x4c, 0xfd, 0xfe, 0x92, 0xbb, 0x57, 0xc5, 0xf7, 0x1f, 0x3c, 0x7c, 0xf4, 0x18, 0x8, 0x8f, 0x9f, 0x3c, 0x7d, 0xf6, 0xfc, 0xfe, 0xb, 0xed, 0x24, 0x66, 0x37, 0xe9, 0x75, 0x4b, 0xb5, 0x77, 0x60, 0xfb, 0xfa, 0x7, 0x54, 0xfc, 0xf2, 0xd5, 0x6b, 0x68, 0xe0, 0xcd, 0xdb, 0x97, 0x2f, 0x2c, 0xdf, 0x62, 0xb0, 0xbf, 0xaf, 0x7a, 0x9, 0xbe, 0xcd, 0x33, 0x84, 0xde, 0xbd, 0x7f, 0x2, 0x4d, 0x7c, 0x78, 0xff, 0xf1, 0x31, 0x58, 0x30, 0x34, 0xdc, 0x36, 0x42, 0x8f, 0xc0, 0x8, 0xae, 0x51, 0xf4, 0xe9, 0xf3, 0x17, 0x68, 0xe1, 0xeb, 0xb7, 0x34, 0x58, 0x31, 0xea, 0x12, 0xa8, 0x1, 0x2b, 0xba, 0xc7, 0x50, 0xe6, 0x19, 0xe8, 0xc8, 0x66, 0x80, 0x92, 0xcb, 0x17, 0x8a, 0x25, 0xd8, 0x2d, 0x94, 0xd1, 0x64, 0xf, 0x8, 0xe3, 0x13, 0x93, 0xf4, 0xe, 0x9c, 0xe4, 0x89, 0xe3, 0xfd, 0x3, 0xd0, 0x71, 0xb0, 0xf, 0x94, 0xc3, 0xa3, 0xdc, 0x71, 0x21, 0xb7, 0x4b, 0x2, 0x35, 0xa1, 0x4c, 0xd, 0x4b, 0x1c, 0x35, 0x90, 0xa7, 0x67, 0xf0, 0xc9, 0x29, 0xe8, 0x38, 0x3d, 0xa9, 0x1c, 0x80, 0x4, 0x1d, 0x9d, 0xed, 0x9e, 0x13, 0x3, 0x22, 0x94, 0xed, 0x59, 0x99, 0xa7, 0x6, 0x6d, 0x73, 0x2a, 0xbe, 0x6d, 0xf0, 0x60, 0x8f, 0x6f, 0x13, 0x25, 0x41, 0x65, 0xb9, 0xc8, 0xef, 0x1e, 0x1e, 0x1e, 0xee, 0x69, 0x2, 0x94, 0xf9, 0xb6, 0x8a, 0x81, 0xec, 0x55, 0x2d, 0x4f, 0xb0, 0x47, 0x4e, 0x90, 0xbf, 0xdc, 0x25, 0x91, 0x44, 0x74, 0x27, 0x90, 0x3c, 0xb, 0xc8, 0xf2, 0xd, 0xce, 0xcf, 0x60, 0xaf, 0x58, 0xaa, 0x18, 0xe8, 0xdf, 0x80, 0x15, 0x27, 0x6c, 0x7e, 0x61, 0xb7, 0x78, 0x5e, 0xcc, 0x43, 0xab, 0x1, 0xfd, 0x5, 0x9a, 0x7, 0x8b, 0x36, 0x79, 0xb0, 0x77, 0x76, 0x5, 0x90, 0x2b, 0xed, 0x55, 0x84, 0xb2, 0x18, 0x16, 0x98, 0x5a, 0x26, 0x2e, 0xe1, 0x5f, 0xce, 0x44, 0x5a, 0xb, 0x83, 0x3f, 0x5f, 0xb, 0xcb, 0xb4, 0x16, 0xcc, 0xab, 0xf1, 0x9a, 0xc6, 0xdb, 0x57, 0x23, 0xed, 0x7, 0x2b, 0xab, 0x2a, 0xba, 0x69, 0xed, 0x7, 0xe6, 0x6c, 0xaf, 0xae, 0xd5, 0xfa, 0x1, 0xed, 0x48, 0x8a, 0x7b, 0x7d, 0x3, 0xa5, 0x10, 0x6d, 0x48, 0xb8, 0x2, 0x2a, 0x2f, 0x30, 0xa2, 0x73, 0xdd, 0xad, 0x24, 0x9b, 0x7b, 0x5a, 0x97, 0x14, 0xf6, 0x44, 0x63, 0x53, 0xdb, 0x60, 0xcb, 0xf6, 0x54, 0x2c, 0xea, 0x9, 0x4b, 0x5d, 0x6c, 0x6b, 0x57, 0xee, 0x93, 0x5d, 0x13, 0xc3, 0xb3, 0x9b, 0x1, 0x1b, 0x36, 0x67, 0x87, 0x27, 0x5c, 0x72, 0x1f, 0xcf, 0xe8, 0xa7, 0xca, 0x88, 0x30, 0xb9, 0xd5, 0x66, 0x3f, 0x17, 0xda, 0xb6, 0x26, 0x85, 0x11, 0x96, 0x31, 0x99, 0x4c, 0xfc, 0xf, 0x40, 0x27, 0xd3, 0xbf, 0xc4, 0x77, 0x82, 0xde, 0x40, 0xde, 0x4b, 0x3f, 0xe2, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char toggle_off_disabled_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x20, 0x8, 0x6, 0x0, 0x0, 0x0, 0x4e, 0xe8, 0x1b, 0x92, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x12, 0x0, 0x0, 0xb, 0x12, 0x1, 0xd2, 0xdd, 0x7e, 0xfc, 0x0, 0x0, 0x6, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0xed, 0x59, 0x4b, 0x6f, 0x1b, 0x55, 0x14, 0x3e, 0xb6, 0xc7, 0x8e, 0xed, 0xf1, 0x2b, 0x7e, 0x84, 0xb8, 0x49, 0x8a, 0x48, 0x36, 0x55, 0x37, 0x48, 0x5d, 0x64, 0xc1, 0x3a, 0x42, 0x82, 0x14, 0x89, 0x56, 0x14, 0x16, 0x48, 0xfc, 0x1, 0xca, 0x1f, 0x40, 0x20, 0x81, 0x90, 0xf8, 0x1, 0xd0, 0x25, 0x8b, 0x8a, 0x25, 0xa0, 0x82, 0x8a, 0x10, 0x12, 0x42, 0x8, 0x29, 0x5d, 0x15, 0xa9, 0x12, 0x8b, 0x4a, 0x20, 0x51, 0x50, 0x1b, 0xcb, 0x79, 0xf8, 0xed, 0x8c, 0xdf, 0xe3, 0x31, 0xe7, 0xbb, 0x99, 0xeb, 0x4c, 0x26, 0x33, 0xb6, 0x13, 0xbb, 0xb4, 0x42, 0xbd, 0xd2, 0xc9, 0x24, 0x77, 0xee, 0x39, 0x73, 0xbf, 0x7b, 0x1e, 0xf7, 0x9c, 0x13, 0xa2, 0x67, 0xe3, 0xd9, 0x9, 0xfc, 0xaf, 0x4e, 0xc0, 0x33, 0x21, 0x1a, 0xac, 0x93, 0x4, 0x96, 0x49, 0xf9, 0x26, 0x14, 0x7f, 0xe6, 0x65, 0x3, 0x93, 0x13, 0x4f, 0x49, 0x23, 0x85, 0x8d, 0xdb, 0xb8, 0x97, 0xb9, 0x41, 0x7e, 0xb, 0xf9, 0xcc, 0xb9, 0x71, 0xbc, 0x67, 0x46, 0x31, 0x21, 0x23, 0x0, 0x1a, 0x4c, 0x7d, 0xa6, 0x9e, 0x85, 0x30, 0x7, 0x72, 0x1c, 0x6e, 0x9b, 0xc6, 0x3c, 0x80, 0x5, 0x98, 0x22, 0x4c, 0x9, 0x93, 0x62, 0xfc, 0xc, 0x99, 0xe0, 0x71, 0x10, 0x4f, 0xa, 0xb4, 0x4, 0xb, 0xa0, 0x2d, 0xa6, 0x3a, 0x53, 0xd5, 0x24, 0x8d, 0x9f, 0x5d, 0xf3, 0x20, 0xa4, 0x5, 0xc, 0xc1, 0x3b, 0x6d, 0x18, 0x40, 0x14, 0x13, 0x68, 0x86, 0x9f, 0xcb, 0xe9, 0xf4, 0x73, 0xd7, 0x22, 0x91, 0xd8, 0x6b, 0x8a, 0xe2, 0xe3, 0xbf, 0x3d, 0x78, 0xe7, 0xe1, 0x31, 0xa1, 0x22, 0x1e, 0xcf, 0xb2, 0xc1, 0x40, 0x60, 0xe1, 0x1f, 0x3, 0x5d, 0xd7, 0xfb, 0x5, 0x4d, 0xab, 0x7f, 0x5f, 0x2c, 0xee, 0x7d, 0xcd, 0x73, 0x39, 0xa6, 0x2, 0x13, 0x80, 0xeb, 0x76, 0x6d, 0xdb, 0x77, 0xd, 0xb0, 0xd0, 0x6a, 0x9c, 0xe9, 0x7c, 0x22, 0x91, 0xba, 0x92, 0x4c, 0xa6, 0xdf, 0xf3, 0x7a, 0x3d, 0x91, 0x23, 0x7c, 0x56, 0x16, 0x37, 0xd0, 0x27, 0xe, 0xf6, 0x70, 0x6f, 0xc2, 0x20, 0x60, 0x6d, 0xb3, 0x37, 0xe, 0x1c, 0x80, 0x61, 0x18, 0x5a, 0xb9, 0x5c, 0xfc, 0xbc, 0x5a, 0x2d, 0x7d, 0xcb, 0x1f, 0x79, 0xc4, 0x54, 0x33, 0xb5, 0x3d, 0x34, 0x71, 0xfb, 0xee, 0x1, 0x76, 0x9e, 0x69, 0x35, 0x9b, 0x5d, 0x7e, 0x5f, 0x55, 0x23, 0x9b, 0x0, 0xea, 0xf7, 0x7b, 0x29, 0x99, 0x4c, 0x52, 0x34, 0x1a, 0xa5, 0xb9, 0xb9, 0x39, 0xf2, 0x7a, 0xb1, 0x61, 0xe7, 0xc1, 0xc7, 0x4d, 0xb5, 0x5a, 0x8d, 0xa, 0x85, 0x2, 0xf5, 0x60, 0x70, 0xc3, 0x21, 0xf, 0x1, 0xdf, 0xc6, 0x67, 0xdd, 0x65, 0x4c, 0x63, 0x13, 0x0, 0xde, 0x68, 0x1c, 0xfc, 0xb0, 0xb3, 0x93, 0xfb, 0x94, 0xe5, 0xfc, 0xcd, 0x54, 0x31, 0x41, 0x8b, 0xd, 0x48, 0xc0, 0xd2, 0x67, 0xa1, 0xd9, 0xb5, 0x73, 0xe7, 0x56, 0x3e, 0x9, 0x87, 0xd5, 0x97, 0x61, 0xb6, 0xe9, 0x74, 0x42, 0x80, 0x5, 0x88, 0x6a, 0xb5, 0x4a, 0xdd, 0x8e, 0x21, 0x74, 0x65, 0x1f, 0x10, 0xa0, 0x70, 0x68, 0xb, 0x6, 0x83, 0xe2, 0x60, 0x40, 0xa5, 0x52, 0x89, 0xca, 0xc5, 0x9a, 0x43, 0x4, 0x91, 0xda, 0x9e, 0x6, 0x9a, 0x3b, 0x2f, 0x40, 0x37, 0x9b, 0xda, 0x4f, 0xf9, 0xfc, 0xf6, 0x87, 0xbc, 0xea, 0x81, 0xa9, 0x69, 0x4, 0xb7, 0x1, 0x2, 0x13, 0x6, 0x8e, 0x3b, 0xc8, 0x94, 0x9d, 0x9f, 0x4f, 0xbf, 0x1d, 0x8f, 0x27, 0xde, 0x1, 0xd8, 0x6c, 0x36, 0x43, 0xaa, 0xaa, 0x52, 0x3e, 0x9f, 0x67, 0xb0, 0xd, 0xea, 0xf7, 0x9d, 0xa0, 0x1e, 0x7d, 0xd8, 0x60, 0xe5, 0x75, 0xbb, 0x3a, 0x1d, 0x1c, 0x34, 0xa9, 0xdd, 0x6e, 0x88, 0x83, 0xa, 0x86, 0x2, 0xd4, 0xd4, 0x9a, 0xb6, 0x43, 0x7a, 0xbc, 0xfe, 0x8f, 0xbd, 0xfb, 0xfd, 0x81, 0x35, 0x0, 0x6d, 0xb5, 0x9a, 0x7f, 0xf2, 0xb3, 0xc1, 0x4, 0x7f, 0x1e, 0x48, 0xbb, 0xc2, 0x13, 0xd1, 0x78, 0x39, 0x99, 0x4c, 0x5d, 0x87, 0x19, 0x27, 0x93, 0x31, 0xa, 0x85, 0x42, 0x94, 0xcb, 0xe5, 0xa8, 0xd5, 0xc2, 0xda, 0xd3, 0x8d, 0x46, 0xa3, 0x2b, 0x78, 0x21, 0x23, 0x91, 0x8c, 0xfe, 0xe7, 0xe1, 0x1c, 0xa0, 0xe7, 0xe7, 0x53, 0xef, 0xf2, 0xae, 0x57, 0x4c, 0x6c, 0x50, 0xae, 0x47, 0x46, 0xf, 0xdc, 0xb3, 0xf1, 0x4c, 0x66, 0xf1, 0x2d, 0xe, 0x50, 0xa1, 0x0, 0x7, 0xe2, 0x54, 0x2a, 0x45, 0x3b, 0x3b, 0x3b, 0x36, 0x3f, 0x3c, 0x1d, 0x68, 0xf8, 0xf0, 0xee, 0xee, 0xae, 0x90, 0xe5, 0x53, 0x46, 0x5b, 0xc7, 0xe9, 0x24, 0x4f, 0xb6, 0xda, 0xeb, 0xf5, 0x85, 0x18, 0xd3, 0x9b, 0xc0, 0x6, 0x8f, 0x3, 0x97, 0xb8, 0x62, 0x98, 0x0, 0x38, 0xa1, 0xaa, 0xd1, 0x4d, 0x4c, 0x46, 0x93, 0x11, 0x76, 0xfc, 0xc6, 0x99, 0x34, 0x6b, 0xdf, 0x4a, 0xb3, 0xd9, 0x63, 0x7f, 0x6a, 0x52, 0x3c, 0x1e, 0x67, 0x9f, 0xc6, 0x75, 0xe9, 0x3c, 0x7a, 0xbd, 0x36, 0xad, 0xaf, 0xaf, 0xd3, 0xc5, 0x8b, 0x17, 0xa8, 0xd3, 0xe9, 0xd0, 0xd6, 0xd6, 0x1d, 0xda, 0xdb, 0x2b, 0xb1, 0x85, 0x4, 0xe8, 0xea, 0xd5, 0xd7, 0x8f, 0x31, 0xdd, 0xba, 0xf5, 0xdd, 0x89, 0xb9, 0xdb, 0xb7, 0x7f, 0x64, 0x57, 0x3a, 0x38, 0x21, 0x1c, 0x81, 0x97, 0xe3, 0xe7, 0x17, 0xfc, 0x62, 0x8f, 0xa9, 0x63, 0x5, 0x1c, 0xe3, 0x7b, 0x36, 0xd, 0xfc, 0xb1, 0x58, 0x8c, 0xca, 0xe5, 0xf2, 0x64, 0xc7, 0x38, 0xc1, 0x2a, 0x4d, 0xd3, 0x84, 0x3f, 0x8f, 0x2, 0xbc, 0xb1, 0xb1, 0x21, 0x80, 0xde, 0xbc, 0xf9, 0x25, 0x7, 0xca, 0x34, 0x5d, 0xbe, 0xfc, 0xaa, 0xf8, 0x1d, 0x80, 0x31, 0xac, 0x80, 0x9c, 0xe6, 0xdc, 0xb6, 0xa1, 0x28, 0xa, 0x72, 0x9, 0x24, 0x4c, 0x2, 0xab, 0xf4, 0x61, 0xd8, 0x37, 0x82, 0x96, 0x50, 0x7b, 0x20, 0xc0, 0x81, 0x86, 0xb5, 0x32, 0xab, 0x1, 0x59, 0x90, 0x39, 0x4a, 0xbb, 0xcb, 0xcb, 0xe7, 0xe8, 0xfe, 0xfd, 0x3f, 0x38, 0xd8, 0x4, 0xf9, 0x46, 0xd0, 0xe8, 0xc1, 0x83, 0x7f, 0x68, 0x75, 0x75, 0x55, 0xb0, 0xec, 0xef, 0x97, 0x4e, 0xb0, 0x3a, 0xcd, 0x39, 0xcb, 0x17, 0x89, 0x12, 0xb2, 0x43, 0xe1, 0xc3, 0x2, 0x20, 0x7e, 0x91, 0x13, 0xf8, 0x83, 0x4f, 0x85, 0xf4, 0x63, 0x77, 0xe8, 0x74, 0xd0, 0x21, 0xb, 0x32, 0xdd, 0x6, 0x2c, 0xa, 0x0, 0xac, 0x26, 0x59, 0x2c, 0x16, 0x84, 0xa6, 0x8b, 0xc5, 0x22, 0x47, 0xfe, 0xe, 0x5d, 0xba, 0xf4, 0xa2, 0x60, 0xbf, 0x77, 0xef, 0x77, 0xd2, 0xf5, 0xce, 0x89, 0x39, 0x27, 0x73, 0x16, 0xc0, 0xe, 0x33, 0xa6, 0x61, 0xa6, 0x23, 0x77, 0x21, 0x73, 0x53, 0x21, 0x14, 0xc9, 0x3, 0xee, 0xd4, 0xe3, 0x89, 0xc3, 0xd9, 0x41, 0x43, 0x16, 0x64, 0xba, 0xd, 0x45, 0x99, 0xa3, 0x85, 0x85, 0x94, 0xb8, 0xbb, 0xe5, 0xc6, 0xa3, 0xd1, 0x98, 0x0, 0x25, 0x7, 0x80, 0xca, 0x77, 0xd2, 0xa4, 0xad, 0x73, 0x6e, 0xb2, 0xcd, 0x14, 0x14, 0xd9, 0x8e, 0x88, 0x9a, 0xd2, 0xa4, 0x71, 0x29, 0x73, 0x12, 0x3e, 0xe0, 0x5d, 0xd, 0xf8, 0x43, 0x5d, 0xa, 0x87, 0xc3, 0x67, 0x47, 0x68, 0xe3, 0x84, 0x2c, 0xc8, 0x74, 0x1b, 0x0, 0x2, 0xd, 0x27, 0x12, 0x51, 0xb1, 0x4, 0xc0, 0xd7, 0xd6, 0x5e, 0x10, 0xda, 0x9d, 0x7e, 0x0, 0x93, 0x28, 0x30, 0x44, 0xe2, 0x1, 0xd, 0x3, 0x39, 0xc, 0xf8, 0x40, 0xd7, 0x8d, 0x82, 0xdf, 0xef, 0xcb, 0xd6, 0xeb, 0x75, 0x91, 0x70, 0xd4, 0x6a, 0xb3, 0xf1, 0xe3, 0x48, 0x24, 0x42, 0x90, 0x39, 0x6a, 0xdc, 0xb9, 0xb3, 0x45, 0x9b, 0x9b, 0xaf, 0xd0, 0x85, 0xb, 0x1a, 0x47, 0xf4, 0x88, 0xf0, 0x61, 0x19, 0xa5, 0xa7, 0x1, 0xcd, 0x96, 0x85, 0x42, 0x2, 0x1f, 0x17, 0x89, 0x87, 0x35, 0xd3, 0xa, 0x70, 0x76, 0xb2, 0xc0, 0xa9, 0xe1, 0xa5, 0x7e, 0x5b, 0xa7, 0xcc, 0x62, 0x86, 0xaf, 0x25, 0x8d, 0x4d, 0xd1, 0xb5, 0xb4, 0x9c, 0x68, 0x1f, 0xe1, 0xb0, 0x5f, 0xdc, 0xc3, 0xb8, 0x8f, 0xd, 0xc3, 0x3d, 0xc3, 0xe2, 0x8a, 0x87, 0xb6, 0xb7, 0xf3, 0xc4, 0x55, 0xf, 0xdd, 0xbd, 0xfb, 0x1b, 0xe7, 0x0, 0xfb, 0x42, 0x7e, 0xab, 0xd5, 0x10, 0xa6, 0xdd, 0xeb, 0xf5, 0x87, 0x56, 0xe2, 0x34, 0xe7, 0x6e, 0x3d, 0xb5, 0xaf, 0x38, 0xcd, 0xfc, 0x85, 0xdf, 0xe3, 0xda, 0xd1, 0x25, 0x60, 0x61, 0xde, 0xfc, 0xa2, 0xc0, 0xa9, 0xe5, 0x35, 0xc3, 0x33, 0x60, 0xaf, 0x33, 0x28, 0x93, 0xc9, 0x88, 0xd, 0x20, 0x65, 0x3c, 0xcb, 0xf0, 0xb3, 0x94, 0xa5, 0xa5, 0x25, 0xaa, 0x54, 0x2a, 0xd4, 0xe4, 0xcc, 0x6b, 0xdc, 0x80, 0xd9, 0xd7, 0xeb, 0x7, 0x34, 0x18, 0x1c, 0x1d, 0x8c, 0xcf, 0xa7, 0x70, 0x4e, 0xd0, 0x3a, 0xe6, 0x12, 0x4e, 0x73, 0x4e, 0xb2, 0xd, 0xa3, 0xdf, 0xca, 0xe5, 0x1e, 0x7e, 0xc0, 0xef, 0x1e, 0x32, 0x21, 0xbd, 0x34, 0xac, 0x80, 0x45, 0x46, 0xcf, 0x41, 0x4d, 0xf, 0x85, 0xc2, 0x2f, 0xb5, 0xdb, 0x5d, 0x2e, 0x4, 0x2, 0xe2, 0xfe, 0xec, 0x74, 0x9a, 0xa7, 0xd6, 0xb4, 0xaa, 0x6, 0x38, 0x17, 0xcf, 0x72, 0x4e, 0xdd, 0xa6, 0x52, 0xa1, 0xea, 0x58, 0x70, 0x8c, 0x3b, 0x80, 0x69, 0xde, 0x23, 0x58, 0x55, 0x2a, 0xc5, 0xcf, 0x38, 0x97, 0xfe, 0x95, 0xe5, 0x20, 0x18, 0xe0, 0xc4, 0x87, 0x26, 0xd, 0xd9, 0xa2, 0x5d, 0xc2, 0xb, 0x76, 0x82, 0xc1, 0xd0, 0xf3, 0x7e, 0xbf, 0x7f, 0x4d, 0xe3, 0xa4, 0x5f, 0x51, 0xbc, 0xb4, 0xb8, 0xb8, 0x28, 0x9e, 0xb8, 0xe, 0xc, 0x97, 0x2, 0x42, 0xa4, 0x6b, 0xac, 0x51, 0x55, 0xd, 0xf2, 0x75, 0x32, 0x2f, 0xac, 0x43, 0x94, 0x89, 0x7b, 0x65, 0xf7, 0x7e, 0xcb, 0x34, 0x88, 0x46, 0xf0, 0xca, 0x6a, 0x69, 0x7f, 0x7f, 0xf7, 0x6, 0x2f, 0x43, 0x43, 0x40, 0x68, 0x17, 0x2c, 0x6e, 0xf5, 0x30, 0x97, 0x88, 0xe7, 0x3f, 0xe2, 0xe8, 0xba, 0x81, 0x6b, 0x4c, 0x51, 0x3c, 0xc2, 0xf, 0xa7, 0xab, 0x87, 0x1f, 0x13, 0x3a, 0x9b, 0xd8, 0x43, 0xb0, 0x8d, 0x9f, 0xf3, 0xf9, 0x47, 0x1f, 0xf3, 0xab, 0xbf, 0x98, 0x1c, 0xeb, 0x61, 0xc9, 0x26, 0x3b, 0x1e, 0xe8, 0x61, 0x9d, 0xe7, 0x6a, 0xe3, 0xa, 0xd3, 0x75, 0x9f, 0xcf, 0xcb, 0x95, 0x94, 0xac, 0x61, 0xad, 0x25, 0xb4, 0xf5, 0x6b, 0xe3, 0x8a, 0x3, 0xc9, 0x6f, 0x97, 0x33, 0xbb, 0x83, 0xe8, 0xf7, 0xfb, 0x1a, 0x9b, 0xf1, 0x8d, 0x4a, 0x65, 0xd8, 0xf1, 0x40, 0x9f, 0xb, 0xa6, 0xec, 0xd8, 0xf1, 0xb0, 0x82, 0x46, 0x31, 0x81, 0x72, 0x91, 0x73, 0x6b, 0x5a, 0xe1, 0x9e, 0xd6, 0x1b, 0x66, 0x4f, 0x6b, 0x1, 0xd9, 0xd9, 0x93, 0xee, 0x67, 0xc9, 0x8d, 0x1e, 0x26, 0x15, 0xa2, 0xa7, 0xb5, 0x6f, 0xf6, 0xb4, 0xbe, 0xe1, 0x89, 0x6d, 0xd3, 0x67, 0xd1, 0xd3, 0xc2, 0x75, 0x7b, 0x2c, 0xe4, 0x8e, 0xeb, 0x5a, 0xce, 0x31, 0x83, 0xca, 0x4, 0x8d, 0xa3, 0xf5, 0x63, 0xef, 0x5a, 0xce, 0x4e, 0x3d, 0xa7, 0x97, 0x4, 0x20, 0xd6, 0xae, 0x25, 0x4c, 0x17, 0x1a, 0x85, 0xbf, 0x22, 0x45, 0x13, 0x89, 0x86, 0x5d, 0xec, 0xa8, 0xd6, 0x3, 0xde, 0xc9, 0x1c, 0x1b, 0x9, 0xa, 0xb2, 0x7f, 0x68, 0x5e, 0x24, 0xe1, 0x26, 0x9d, 0x7e, 0x9b, 0xb3, 0xe3, 0x90, 0x8d, 0x77, 0xd9, 0x97, 0x86, 0xe9, 0x22, 0xb9, 0x90, 0x40, 0x1d, 0x7d, 0x6c, 0xd2, 0x5e, 0x8b, 0x4, 0xf8, 0x34, 0x0, 0xb5, 0x1f, 0x99, 0xf5, 0xbf, 0xe, 0xe3, 0x2, 0xc9, 0xec, 0x8e, 0xfb, 0x99, 0xa4, 0xa7, 0xf4, 0x4, 0xfe, 0x5, 0x72, 0xf1, 0x9c, 0xca, 0xa6, 0xf4, 0x2a, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char toggle_on_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 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, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char toggle_on_disabled_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x6, 0x0, 0x0, 0x0, 0xa2, 0x9d, 0x7e, 0x84, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x12, 0x0, 0x0, 0xb, 0x12, 0x1, 0xd2, 0xdd, 0x7e, 0xfc, 0x0, 0x0, 0x6, 0x54, 0x49, 0x44, 0x41, 0x54, 0x68, 0x43, 0xed, 0x59, 0x4b, 0x6f, 0x1b, 0x55, 0x14, 0x3e, 0x71, 0xfc, 0x9a, 0xd8, 0x4e, 0x9c, 0x38, 0x6e, 0x62, 0xe7, 0x21, 0xb5, 0x6e, 0x51, 0xd5, 0x5, 0xa8, 0xac, 0x93, 0x14, 0x89, 0x22, 0x84, 0xba, 0x60, 0x53, 0xb1, 0x29, 0x12, 0x42, 0xa0, 0xfe, 0x1, 0x7e, 0x1, 0x7b, 0xb6, 0x6c, 0xaa, 0x82, 0xa8, 0xc4, 0x96, 0x5f, 0x50, 0x21, 0x55, 0x34, 0x1b, 0x4a, 0x17, 0x65, 0x51, 0x9, 0x85, 0x3c, 0x9b, 0xb4, 0x4d, 0xfd, 0x76, 0xfc, 0x7e, 0xf3, 0x7d, 0x37, 0x33, 0xc6, 0x76, 0xed, 0xcc, 0x38, 0x4e, 0x52, 0x21, 0x7a, 0xa4, 0xa3, 0xc9, 0x4c, 0xee, 0x3d, 0xf7, 0xbc, 0xee, 0xb9, 0xdf, 0x3d, 0x16, 0x79, 0x4b, 0x6f, 0x3d, 0xf0, 0xbf, 0xf6, 0xc0, 0xc8, 0x80, 0xd6, 0x73, 0xbc, 0xc1, 0x9c, 0x3a, 0xe8, 0xfc, 0x1, 0x97, 0xb3, 0x3c, 0xbc, 0xa9, 0x8f, 0xe4, 0xd3, 0x60, 0x4b, 0x93, 0xad, 0x1a, 0x60, 0x83, 0x34, 0xb2, 0xa3, 0x8d, 0x47, 0xf5, 0x6f, 0x56, 0x65, 0x58, 0x52, 0xe8, 0x18, 0x83, 0x68, 0x70, 0x3, 0x5c, 0x7, 0x57, 0xdb, 0x98, 0xdf, 0xc8, 0x47, 0x92, 0x99, 0xf2, 0xfc, 0x3f, 0xd, 0x75, 0x82, 0xbd, 0x60, 0xbf, 0xce, 0xe3, 0x78, 0x6a, 0xba, 0x33, 0xe8, 0x18, 0x33, 0x39, 0x66, 0x7a, 0x1c, 0xf7, 0xff, 0x86, 0xf1, 0x34, 0xbc, 0x8, 0x3e, 0x0, 0xa7, 0x75, 0xce, 0xe1, 0x59, 0xd1, 0x1d, 0x63, 0x64, 0xc8, 0x6b, 0xeb, 0x1c, 0xa5, 0x38, 0xd, 0xb3, 0xeb, 0x86, 0x7, 0xf1, 0x9c, 0xf7, 0xf9, 0x26, 0x96, 0xc1, 0x1f, 0x39, 0x9d, 0xce, 0xb, 0x36, 0xdb, 0xe8, 0xb8, 0xcd, 0x66, 0xd3, 0x46, 0x40, 0xc7, 0xd5, 0xfe, 0x24, 0xe6, 0x35, 0x41, 0x8d, 0x46, 0xa3, 0xd8, 0x68, 0xd4, 0xf, 0x2a, 0x95, 0xca, 0x66, 0x36, 0x9b, 0xb9, 0xf, 0x7e, 0x8, 0xd9, 0x7b, 0xe0, 0x18, 0x98, 0x8e, 0xa8, 0xf5, 0xcb, 0x86, 0x7e, 0xca, 0xd3, 0x78, 0x46, 0x7d, 0x2, 0xbc, 0x38, 0x36, 0xe6, 0x5d, 0x9, 0x4, 0x82, 0xb7, 0x5d, 0x2e, 0xf7, 0x3b, 0x6f, 0xd8, 0x5e, 0x53, 0x9f, 0xc1, 0x1f, 0x52, 0x2e, 0x97, 0xd6, 0x12, 0x89, 0xd8, 0x9d, 0x42, 0x21, 0xf7, 0x1b, 0x26, 0x3c, 0x3, 0x67, 0xf4, 0x6c, 0x78, 0x6d, 0x4b, 0xf4, 0x72, 0x0, 0xbf, 0xd1, 0xf8, 0x49, 0xf0, 0x85, 0xc9, 0xc9, 0xe9, 0xcf, 0xa7, 0xa6, 0xa6, 0xbf, 0x42, 0xb4, 0xf9, 0xed, 0x4c, 0x49, 0xd3, 0xec, 0x32, 0x3b, 0x3b, 0x2b, 0x93, 0x93, 0x93, 0xa2, 0x69, 0x9a, 0x40, 0x87, 0xbe, 0xeb, 0xd7, 0x6a, 0x35, 0x89, 0xc7, 0xe3, 0xb2, 0xb7, 0xb7, 0x27, 0xc5, 0x62, 0x4d, 0x90, 0x15, 0x95, 0x64, 0x32, 0xfe, 0x43, 0x2a, 0x15, 0xff, 0x19, 0x93, 0x36, 0xc1, 0x29, 0xdd, 0x9, 0x1d, 0xdb, 0xa1, 0xdb, 0x1, 0xc6, 0x9e, 0x67, 0xe4, 0x23, 0x53, 0x53, 0xc1, 0xaf, 0x61, 0xfc, 0xd7, 0x67, 0x9e, 0xe6, 0xb6, 0x51, 0x59, 0x98, 0xb, 0xca, 0xcc, 0xcc, 0x8c, 0x24, 0x12, 0x9, 0x65, 0x58, 0x36, 0x87, 0x6d, 0xde, 0xec, 0x51, 0xd3, 0xb0, 0x3, 0x35, 0xb7, 0x4d, 0x3c, 0x1e, 0x8f, 0xf8, 0xfd, 0x7e, 0xe5, 0xac, 0xfd, 0xfd, 0x7d, 0xd9, 0x7d, 0x1e, 0x93, 0x66, 0xbd, 0xd6, 0x84, 0x13, 0xee, 0x26, 0x93, 0xb1, 0xbb, 0xb0, 0x67, 0x43, 0xcf, 0x4, 0x16, 0xcb, 0x96, 0x13, 0x58, 0xe0, 0xda, 0x89, 0x2e, 0x76, 0x83, 0x43, 0x1e, 0x8f, 0xef, 0xe3, 0x60, 0x70, 0xe6, 0x1b, 0x78, 0xbd, 0x7b, 0xcc, 0xe9, 0x66, 0x1, 0x96, 0x8b, 0x9c, 0x9f, 0x93, 0x89, 0x89, 0x9, 0xd9, 0xdc, 0xdc, 0x84, 0x31, 0x9, 0xa9, 0x54, 0xb8, 0x85, 0xfb, 0xd5, 0xb1, 0xa6, 0xd4, 0x6a, 0xd, 0x29, 0x14, 0xca, 0x92, 0x4c, 0x66, 0xf0, 0xcc, 0x4a, 0x28, 0x14, 0x12, 0xaf, 0xc7, 0x2d, 0xa9, 0x4c, 0x7e, 0xc4, 0xed, 0x72, 0xbd, 0x57, 0xa9, 0x94, 0xb7, 0xaa, 0xd5, 0xca, 0x2e, 0x84, 0xe4, 0xc1, 0x1d, 0xc2, 0xba, 0x8d, 0xe3, 0x3b, 0x2b, 0xfd, 0xa5, 0x50, 0x68, 0xfe, 0x5b, 0x87, 0xc3, 0x11, 0x38, 0x5d, 0x6b, 0xbb, 0xa4, 0x23, 0x9a, 0xe1, 0x50, 0x40, 0x45, 0x71, 0x6d, 0x6d, 0x4d, 0x72, 0xb9, 0xf2, 0xc0, 0xcb, 0x97, 0x4a, 0x55, 0x39, 0x38, 0x48, 0x29, 0x27, 0x8c, 0xda, 0x9a, 0x92, 0xcb, 0x97, 0x6c, 0x28, 0xda, 0x91, 0x4c, 0x26, 0xc5, 0x7a, 0xc0, 0x6d, 0x40, 0xa1, 0x2d, 0x6f, 0xb6, 0x6f, 0x2a, 0xa6, 0x3f, 0xcf, 0xf9, 0x89, 0xf1, 0x71, 0xff, 0xa, 0xa, 0xde, 0xf9, 0x81, 0x57, 0x1f, 0x72, 0x82, 0xcb, 0x39, 0xa2, 0x14, 0xdf, 0xd9, 0xd9, 0x51, 0xfb, 0xf8, 0xb8, 0xc4, 0xb9, 0x94, 0x11, 0xe, 0x87, 0x85, 0x32, 0x69, 0xb, 0x6c, 0x5a, 0xa6, 0x6d, 0x60, 0x9e, 0x6c, 0x2d, 0xea, 0xe5, 0x0, 0xbf, 0xd7, 0x3b, 0x7e, 0xdd, 0xea, 0xe2, 0x73, 0x73, 0x61, 0xb9, 0x7a, 0xf5, 0x5d, 0xb9, 0x75, 0xeb, 0x33, 0xb9, 0x79, 0xf3, 0x53, 0xec, 0xdb, 0xc3, 0xa4, 0xd1, 0x34, 0xa7, 0xfa, 0x66, 0x43, 0x14, 0x8c, 0xf7, 0x6b, 0xd7, 0x96, 0x8e, 0x14, 0x1b, 0xc, 0x6, 0x11, 0xbd, 0x3, 0x49, 0xa7, 0x99, 0xa9, 0xc3, 0x11, 0x65, 0x64, 0x32, 0x19, 0xa1, 0x4c, 0x92, 0xd7, 0xeb, 0xa3, 0x4d, 0x2c, 0xec, 0x2c, 0xe6, 0xad, 0xda, 0xd7, 0xcb, 0x1, 0xe3, 0x48, 0x19, 0xcb, 0xd1, 0xbf, 0x78, 0xf1, 0x2, 0x3c, 0xec, 0x92, 0x7, 0xf, 0x56, 0xe5, 0xfe, 0xfd, 0x5f, 0xe5, 0xfa, 0xf5, 0xf, 0x85, 0x4e, 0x31, 0x68, 0x79, 0x99, 0x8e, 0xb7, 0x46, 0x4c, 0xfd, 0x74, 0x9a, 0x38, 0xe6, 0x64, 0x88, 0xb2, 0x28, 0x93, 0xe4, 0x74, 0xba, 0x22, 0x78, 0x10, 0xc0, 0x31, 0x3, 0x7a, 0x3a, 0x80, 0xe3, 0x58, 0x3, 0xdc, 0x4, 0x39, 0x56, 0x54, 0xa0, 0xa1, 0xf3, 0xf3, 0x61, 0x79, 0xfa, 0xf4, 0x2f, 0x79, 0xfe, 0xfc, 0x5, 0x3c, 0x9e, 0x93, 0x8d, 0x8d, 0x2d, 0x44, 0xfd, 0x70, 0x76, 0x34, 0x9a, 0x40, 0x4, 0x2, 0x2a, 0x1b, 0xac, 0x10, 0x8f, 0xba, 0x7c, 0x7e, 0xf8, 0xe8, 0x1b, 0x6b, 0x51, 0x16, 0x65, 0x2a, 0xc3, 0x46, 0xed, 0xac, 0x6d, 0x7c, 0xa1, 0x8d, 0x7d, 0x1d, 0xa0, 0x8e, 0x41, 0x22, 0x3c, 0x2b, 0xa, 0xe3, 0x88, 0x51, 0x46, 0x66, 0xb3, 0xd9, 0xd6, 0xf0, 0x78, 0x3c, 0x26, 0xd3, 0xd3, 0xd3, 0xea, 0x1d, 0xd5, 0x57, 0x9e, 0x3c, 0xf9, 0x53, 0x96, 0x96, 0xac, 0x65, 0x1, 0x8a, 0xae, 0x14, 0x8a, 0xa6, 0xf0, 0xdd, 0x8a, 0x6a, 0x6a, 0xc, 0x65, 0x51, 0x26, 0x49, 0xc7, 0x31, 0xaf, 0xc1, 0xf6, 0x6e, 0x64, 0xa1, 0xb0, 0x35, 0xd0, 0x14, 0xb1, 0xb5, 0x29, 0xd9, 0xed, 0x2e, 0x39, 0x77, 0x2e, 0x20, 0x3e, 0x9f, 0xaf, 0x35, 0xd6, 0xe7, 0x1b, 0x57, 0x86, 0x1b, 0xb4, 0xbe, 0xbe, 0xcd, 0xf4, 0x93, 0xcb, 0x97, 0x2f, 0x9b, 0xca, 0xab, 0x56, 0xab, 0x32, 0xa6, 0xf5, 0x7, 0x3b, 0xa6, 0x2, 0xba, 0x6, 0x50, 0x16, 0x65, 0x92, 0x8, 0x8c, 0xf8, 0x0, 0x77, 0x9c, 0xa7, 0xdd, 0xab, 0x11, 0x24, 0x14, 0xeb, 0xf5, 0x9a, 0xa5, 0x8d, 0xc8, 0xc8, 0x33, 0x3, 0xfc, 0xfe, 0x43, 0x7, 0xd0, 0x11, 0x91, 0xc8, 0x79, 0x5, 0x5c, 0xda, 0xe9, 0xf1, 0xe3, 0xdf, 0xe5, 0xca, 0x15, 0x73, 0x7, 0x14, 0x8b, 0x45, 0x5, 0x68, 0x4e, 0x8a, 0x28, 0x8b, 0x32, 0x49, 0xba, 0x4d, 0x7c, 0xe9, 0x0, 0x42, 0xed, 0x47, 0x2, 0x3d, 0x43, 0x77, 0x65, 0x71, 0xa9, 0xd8, 0x70, 0x38, 0x9c, 0xe7, 0xac, 0x28, 0xb2, 0xba, 0xfa, 0x50, 0x6e, 0xdc, 0xf8, 0x4, 0x11, 0xce, 0x1, 0xbc, 0x78, 0x55, 0xd, 0x78, 0xf5, 0x2a, 0xd1, 0xb1, 0xef, 0xf9, 0x4e, 0x47, 0x99, 0x51, 0x2a, 0x95, 0x52, 0x68, 0x2e, 0x16, 0xb3, 0xe4, 0x7f, 0x33, 0x71, 0x4a, 0x16, 0x65, 0x92, 0x90, 0x95, 0x44, 0x82, 0xbc, 0x2d, 0x1e, 0x9, 0x84, 0xd4, 0x25, 0x8, 0xd0, 0xd7, 0x87, 0x63, 0x63, 0xc5, 0x74, 0x5, 0x4a, 0xab, 0xd5, 0x65, 0x77, 0xf7, 0x5, 0x40, 0xcb, 0x81, 0x3c, 0x7a, 0xf4, 0x87, 0xbc, 0x7c, 0x19, 0x55, 0xd3, 0x8, 0x63, 0xa3, 0xd1, 0x7d, 0x14, 0xb5, 0xc3, 0x8, 0x6c, 0x6f, 0x6f, 0x22, 0x1a, 0x85, 0xd6, 0x7b, 0x2f, 0xd9, 0xe5, 0x72, 0x51, 0x16, 0x16, 0x16, 0x30, 0x2e, 0x27, 0x4, 0x34, 0xc3, 0x90, 0xdf, 0xef, 0x51, 0x38, 0x60, 0x6b, 0x6b, 0xb, 0xd1, 0x6f, 0xc2, 0x11, 0x89, 0x7b, 0xb8, 0x24, 0x3d, 0x82, 0xcc, 0xa4, 0xee, 0x4, 0x25, 0xbe, 0x17, 0xcc, 0xb5, 0x61, 0x60, 0x11, 0xe, 0x58, 0xb6, 0xdb, 0xed, 0x87, 0x67, 0x88, 0x9, 0x21, 0x63, 0x70, 0x7e, 0x67, 0xa5, 0xd9, 0xfc, 0xf7, 0x6a, 0xc1, 0xed, 0x61, 0x18, 0xcf, 0xe9, 0xfc, 0x5f, 0xfb, 0x7b, 0x2f, 0x91, 0x75, 0xec, 0xd0, 0x11, 0x6c, 0x53, 0x2a, 0x4e, 0x34, 0x47, 0x88, 0x7b, 0x1c, 0xe2, 0x25, 0x2a, 0x12, 0x89, 0x20, 0x13, 0x5f, 0x49, 0x3a, 0x53, 0xe0, 0xed, 0x70, 0x2b, 0x1a, 0x7d, 0xf9, 0x3d, 0x64, 0xed, 0x80, 0x79, 0xcc, 0xb4, 0x4, 0xf7, 0x72, 0x0, 0xb7, 0xc2, 0x8, 0x6e, 0x57, 0x39, 0xdc, 0x7, 0x3e, 0x40, 0x36, 0x9c, 0x5c, 0x55, 0xb2, 0x60, 0x4d, 0x36, 0x5f, 0x56, 0x38, 0x9e, 0x88, 0xb0, 0x54, 0xca, 0x43, 0xf9, 0xc1, 0x32, 0x81, 0x91, 0xa7, 0xf1, 0xb9, 0x5c, 0x4e, 0x9e, 0xed, 0x45, 0xa5, 0x51, 0xaf, 0xd5, 0x62, 0xb1, 0xfd, 0xef, 0x70, 0x17, 0x60, 0xf4, 0x59, 0x9c, 0x58, 0xc, 0xfb, 0x5e, 0x86, 0xa8, 0xa2, 0x6a, 0x2f, 0x61, 0x42, 0x86, 0xc6, 0x6b, 0xda, 0xd8, 0xfb, 0x67, 0x7a, 0x1b, 0xc4, 0x7d, 0x3e, 0x5, 0x3c, 0xe1, 0x74, 0xd8, 0x64, 0x71, 0x71, 0x11, 0x20, 0xcb, 0x81, 0x14, 0xae, 0x48, 0xa5, 0x6a, 0xe8, 0xdc, 0xde, 0x96, 0xc4, 0xdf, 0x88, 0x8f, 0xa6, 0x8d, 0xaa, 0x42, 0x1c, 0xe, 0xcf, 0x2, 0x84, 0xcd, 0xa1, 0x86, 0xc4, 0x64, 0xfb, 0xd9, 0xbe, 0xba, 0xd, 0x22, 0xf5, 0x7f, 0x44, 0x36, 0xfd, 0x2, 0x9b, 0xd8, 0x20, 0xe9, 0x88, 0x3e, 0x8d, 0x35, 0xeb, 0x7, 0x44, 0xd0, 0x8, 0xf9, 0xd2, 0xef, 0xf, 0x7c, 0x81, 0x73, 0xb4, 0x3, 0x43, 0x5b, 0x8, 0xe6, 0xd0, 0x43, 0x86, 0xec, 0x7, 0xd4, 0xd2, 0xe9, 0xc4, 0x3d, 0x34, 0x46, 0x7e, 0x82, 0x22, 0xeb, 0x60, 0x4b, 0xfd, 0x0, 0x43, 0x69, 0xa3, 0x23, 0x44, 0xf4, 0xb4, 0xe8, 0xf1, 0x78, 0x57, 0xd0, 0x1b, 0xb8, 0xed, 0x76, 0x6b, 0x97, 0x86, 0xb6, 0xea, 0xc, 0x4, 0x94, 0x4a, 0xc5, 0xbf, 0xd1, 0x3, 0xb8, 0x93, 0xcf, 0xb7, 0x3a, 0x42, 0x3c, 0x56, 0xc, 0x1c, 0xd0, 0xa1, 0x81, 0x59, 0x4f, 0x90, 0x30, 0x8a, 0xcd, 0x50, 0x42, 0xbb, 0x5, 0xf4, 0x3, 0x97, 0xf4, 0x9e, 0xe0, 0x45, 0x42, 0xcb, 0x37, 0xd1, 0x25, 0xea, 0xe5, 0x3f, 0x82, 0x1c, 0x9e, 0xf3, 0x28, 0xc6, 0xeb, 0x7a, 0x4f, 0x70, 0x15, 0xe3, 0x78, 0xff, 0xe7, 0x9e, 0x67, 0x4f, 0x90, 0x85, 0xa4, 0x67, 0x45, 0x35, 0x6b, 0x68, 0x1a, 0x1d, 0x22, 0x17, 0x4, 0x10, 0xa1, 0x30, 0x23, 0x78, 0x32, 0x74, 0x77, 0x85, 0xcf, 0x20, 0xae, 0x7d, 0x97, 0xa0, 0x61, 0xed, 0x5d, 0x61, 0xa6, 0x3a, 0x23, 0xce, 0xfd, 0x4e, 0x48, 0xda, 0x1, 0x7c, 0xba, 0xa5, 0x98, 0x39, 0x80, 0xe3, 0x8d, 0xaa, 0xc3, 0x13, 0x83, 0x75, 0x80, 0x37, 0x1b, 0x66, 0x86, 0x71, 0xa9, 0xb0, 0x22, 0xe3, 0x34, 0x1d, 0x64, 0xfc, 0x10, 0x62, 0xfc, 0x2e, 0xc0, 0x54, 0x27, 0xd8, 0x31, 0xc, 0xef, 0xdb, 0x12, 0x37, 0x8c, 0x1b, 0x44, 0xb9, 0xae, 0x12, 0x3c, 0xc8, 0xd4, 0x53, 0x1f, 0xdb, 0xfe, 0xab, 0xd0, 0x91, 0x46, 0x9f, 0xba, 0x26, 0x6f, 0x17, 0xf8, 0xf, 0x79, 0xe0, 0x1f, 0xd, 0x80, 0x80, 0xb4, 0xad, 0xe9, 0x2a, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char tool_button_pressed_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, 0x1, 0xfe, 0x50, 0x4c, 0x54, 0x45, 0x29, 0x3a, 0x40, 0x2d, 0x3e, 0x44, 0x26, 0x34, 0x3b, 0x24, 0x34, 0x39, 0x23, 0x31, 0x38, 0x22, 0x31, 0x37, 0x22, 0x31, 0x37, 0x22, 0x30, 0x36, 0x22, 0x31, 0x36, 0x26, 0x34, 0x3c, 0x32, 0x44, 0x4c, 0x26, 0x34, 0x39, 0x23, 0x31, 0x36, 0x21, 0x2e, 0x34, 0x1f, 0x2c, 0x30, 0x1f, 0x2b, 0x2f, 0x1f, 0x2a, 0x2e, 0x1e, 0x2b, 0x2f, 0x1f, 0x2b, 0x2e, 0x36, 0x4b, 0x52, 0x25, 0x33, 0x38, 0x20, 0x2f, 0x32, 0x1c, 0x29, 0x2e, 0x1b, 0x26, 0x2a, 0x1a, 0x23, 0x26, 0x18, 0x22, 0x26, 0x19, 0x22, 0x26, 0x19, 0x23, 0x26, 0x19, 0x26, 0x29, 0x20, 0x2d, 0x32, 0x25, 0x31, 0x38, 0x3c, 0x51, 0x59, 0x23, 0x31, 0x37, 0x1f, 0x2b, 0x31, 0x1a, 0x25, 0x2b, 0x17, 0x20, 0x24, 0x15, 0x1c, 0x21, 0x14, 0x1b, 0x21, 0x14, 0x1c, 0x21, 0x13, 0x1b, 0x21, 0x15, 0x1d, 0x21, 0x1a, 0x25, 0x2a, 0x40, 0x57, 0x60, 0x23, 0x31, 0x36, 0x1f, 0x2b, 0x31, 0x1b, 0x25, 0x29, 0x16, 0x1e, 0x23, 0x14, 0x1b, 0x1d, 0x12, 0x19, 0x1d, 0x12, 0x1b, 0x1d, 0x14, 0x1a, 0x1d, 0x45, 0x5e, 0x67, 0x22, 0x32, 0x37, 0x20, 0x2d, 0x31, 0x1a, 0x26, 0x2a, 0x15, 0x1f, 0x25, 0x14, 0x1c, 0x1f, 0x12, 0x1b, 0x1f, 0x12, 0x1b, 0x20, 0x14, 0x1b, 0x1f, 0x15, 0x1e, 0x24, 0x1a, 0x25, 0x29, 0x4b, 0x64, 0x6d, 0x23, 0x32, 0x38, 0x20, 0x2e, 0x32, 0x1b, 0x27, 0x2b, 0x17, 0x22, 0x27, 0x16, 0x1e, 0x23, 0x14, 0x1e, 0x23, 0x16, 0x20, 0x24, 0x14, 0x1e, 0x22, 0x15, 0x1e, 0x22, 0x17, 0x21, 0x27, 0x1c, 0x27, 0x2c, 0x4f, 0x6a, 0x75, 0x21, 0x2f, 0x33, 0x1d, 0x29, 0x2d, 0x19, 0x23, 0x2a, 0x18, 0x22, 0x27, 0x16, 0x21, 0x27, 0x18, 0x23, 0x29, 0x17, 0x21, 0x26, 0x19, 0x23, 0x29, 0x1c, 0x28, 0x2d, 0x21, 0x2e, 0x33, 0x54, 0x70, 0x7c, 0x23, 0x33, 0x38, 0x22, 0x30, 0x34, 0x1e, 0x2a, 0x2f, 0x1a, 0x26, 0x2d, 0x1a, 0x25, 0x2b, 0x19, 0x25, 0x2b, 0x1a, 0x26, 0x2d, 0x1a, 0x26, 0x2c, 0x18, 0x25, 0x2a, 0x1a, 0x24, 0x2a, 0x1a, 0x25, 0x2c, 0x1d, 0x2a, 0x2f, 0x22, 0x2f, 0x34, 0x59, 0x77, 0x82, 0x23, 0x33, 0x39, 0x22, 0x30, 0x35, 0x1f, 0x2c, 0x31, 0x1c, 0x28, 0x30, 0x1c, 0x28, 0x2e, 0x1b, 0x29, 0x2f, 0x1c, 0x2a, 0x31, 0x1b, 0x28, 0x2f, 0x1c, 0x28, 0x2d, 0x1b, 0x27, 0x2f, 0x1f, 0x2b, 0x31, 0x5e, 0x7d, 0x8a, 0x24, 0x34, 0x39, 0x21, 0x2f, 0x37, 0x20, 0x2d, 0x34, 0x1d, 0x2b, 0x33, 0x1d, 0x2b, 0x32, 0x1d, 0x2d, 0x35, 0x1e, 0x2e, 0x36, 0x1f, 0x2e, 0x36, 0x1d, 0x2b, 0x34, 0x1d, 0x2b, 0x31, 0x1d, 0x2b, 0x32, 0x20, 0x2d, 0x32, 0x21, 0x2f, 0x36, 0x63, 0x83, 0x90, 0x25, 0x34, 0x39, 0x21, 0x31, 0x36, 0x1f, 0x2e, 0x34, 0x1f, 0x2e, 0x34, 0x1f, 0x2e, 0x36, 0x20, 0x31, 0x39, 0x21, 0x33, 0x3b, 0x21, 0x32, 0x3b, 0x1f, 0x30, 0x37, 0x1f, 0x2e, 0x35, 0x1e, 0x2d, 0x33, 0x1f, 0x2d, 0x33, 0x21, 0x30, 0x36, 0x67, 0x8a, 0x97, 0x24, 0x33, 0x39, 0x20, 0x30, 0x36, 0x1f, 0x2f, 0x35, 0x21, 0x30, 0x37, 0x22, 0x32, 0x39, 0x21, 0x35, 0x3e, 0x24, 0x37, 0x41, 0x24, 0x36, 0x41, 0x21, 0x33, 0x3c, 0x21, 0x31, 0x38, 0x1e, 0x2f, 0x35, 0x1e, 0x2e, 0x35, 0x20, 0x2e, 0x35, 0x24, 0x31, 0x39, 0x6c, 0x90, 0x9e, 0x22, 0x30, 0x36, 0x1f, 0x2e, 0x36, 0x20, 0x30, 0x36, 0x20, 0x31, 0x39, 0x23, 0x34, 0x3d, 0x23, 0x37, 0x41, 0x26, 0x3c, 0x47, 0x26, 0x3b, 0x46, 0x22, 0x35, 0x3f, 0x22, 0x32, 0x3b, 0x1f, 0x30, 0x37, 0x1f, 0x2e, 0x35, 0x1f, 0x2d, 0x35, 0x21, 0x30, 0x36, 0x72, 0x96, 0xa5, 0x7e, 0x8c, 0xc3, 0xb0, 0x0, 0x0, 0x0, 0xaa, 0x74, 0x52, 0x4e, 0x53, 0xc3, 0xc3, 0xe6, 0xd7, 0xcb, 0xc3, 0xbf, 0xbe, 0xbd, 0xe5, 0xc3, 0xd7, 0xc0, 0xac, 0xa0, 0x9a, 0x98, 0x98, 0x98, 0xc3, 0xcb, 0xac, 0x92, 0x82, 0x7b, 0x78, 0x78, 0x7b, 0x82, 0xac, 0xcb, 0xc3, 0xc3, 0xa0, 0x82, 0x6f, 0x67, 0x64, 0x63, 0x64, 0x67, 0x82, 0xc3, 0xbf, 0x9a, 0x7a, 0x67, 0x5e, 0x5b, 0x5a, 0x5e, 0xc3, 0xbd, 0x98, 0x78, 0x64, 0x5b, 0x57, 0x57, 0x5b, 0x64, 0x78, 0xc3, 0xbd, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x57, 0x5a, 0x63, 0x77, 0xc3, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x5a, 0x63, 0x77, 0x98, 0xc3, 0xbd, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x5a, 0x63, 0x77, 0x98, 0xc3, 0xbd, 0x98, 0x77, 0x63, 0x5a, 0x57, 0x56, 0x57, 0x5a, 0x63, 0x77, 0xc3, 0xbb, 0x96, 0x76, 0x63, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x5a, 0x63, 0x76, 0x96, 0xc3, 0xb5, 0x92, 0x75, 0x62, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x59, 0x62, 0x74, 0x92, 0xc3, 0xa9, 0x8b, 0x71, 0x61, 0x59, 0x57, 0x56, 0x56, 0x57, 0x59, 0x61, 0x71, 0x8b, 0xa9, 0xc3, 0x95, 0x7e, 0x6b, 0x5e, 0x59, 0x57, 0x56, 0x56, 0x57, 0x59, 0x5e, 0x6b, 0x7e, 0x95, 0xc3, 0x4f, 0x78, 0x99, 0x30, 0x0, 0x0, 0x0, 0x67, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5c, 0x8b, 0x5, 0x2, 0xc3, 0x30, 0xc, 0xc4, 0x72, 0x5d, 0x39, 0xf4, 0xff, 0x67, 0x8e, 0x79, 0x1a, 0x37, 0xa0, 0xa0, 0x6d, 0x5d, 0x6b, 0x2a, 0x5a, 0xfd, 0x30, 0xe6, 0x1, 0xf4, 0xa7, 0x76, 0xfa, 0x37, 0x74, 0x7, 0x98, 0x52, 0x83, 0x46, 0x97, 0x41, 0xfb, 0xd6, 0x2d, 0x86, 0xae, 0xfe, 0x84, 0x6b, 0x6d, 0x32, 0x6e, 0x58, 0x28, 0x22, 0x3a, 0x41, 0x32, 0xde, 0xd7, 0x6a, 0x67, 0x5b, 0xb7, 0xb7, 0xc9, 0xb0, 0xd8, 0xd6, 0x3a, 0x65, 0x34, 0xb4, 0x21, 0x8f, 0x1c, 0x3, 0x6d, 0x21, 0x84, 0x5d, 0x32, 0x8a, 0x48, 0x22, 0x6e, 0xda, 0xa8, 0x92, 0x36, 0x3e, 0x87, 0xea, 0x7b, 0x7e, 0x0, 0x62, 0xa8, 0x25, 0xad, 0x68, 0x1d, 0x7d, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
diff --git a/scene/resources/default_theme/toggle_off_disabled.png b/scene/resources/default_theme/toggle_off_disabled.png
new file mode 100644
index 0000000000..d65a9d8e64
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled.png b/scene/resources/default_theme/toggle_on_disabled.png
new file mode 100644
index 0000000000..ca4dbe211f
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled.png
Binary files differ
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index fd7b67a218..8ee9879055 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -1008,7 +1008,7 @@ void DynamicFont::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fallback_count"), &DynamicFont::get_fallback_count);
ADD_GROUP("Settings", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_RANGE, "1,255,1"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,255,1"), "set_outline_size", "get_outline_size");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "get_use_mipmaps");
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 17609ed505..52dfffda5b 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1294,8 +1294,8 @@ void Environment::_bind_methods() {
Environment::Environment() :
bg_mode(BG_CLEAR_COLOR),
tone_mapper(TONE_MAPPER_LINEAR),
- ssao_blur(SSAO_BLUR_DISABLED),
- ssao_quality(SSAO_QUALITY_LOW),
+ ssao_blur(SSAO_BLUR_3x3),
+ ssao_quality(SSAO_QUALITY_MEDIUM),
glow_blend_mode(GLOW_BLEND_MODE_ADDITIVE),
dof_blur_far_quality(DOF_BLUR_QUALITY_LOW),
dof_blur_near_quality(DOF_BLUR_QUALITY_LOW) {
@@ -1346,7 +1346,7 @@ Environment::Environment() :
ssao_ao_channel_affect = 0.0;
ssao_blur = SSAO_BLUR_3x3;
set_ssao_edge_sharpness(4);
- set_ssao_quality(SSAO_QUALITY_LOW);
+ set_ssao_quality(SSAO_QUALITY_MEDIUM);
glow_enabled = false;
glow_levels = (1 << 2) | (1 << 4);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index c72ccc97db..627397f0ab 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -96,6 +96,7 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint);
ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size);
+ ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "p_width"), &Font::get_wordwrap_string_size);
ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false));
ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
@@ -495,9 +496,47 @@ Size2 Font::get_string_size(const String &p_string) const {
return Size2(w, get_height());
}
+
+Size2 Font::get_wordwrap_string_size(const String &p_string, float p_width) const {
+
+ ERR_FAIL_COND_V(p_width <= 0, Vector2(0, get_height()));
+
+ int l = p_string.length();
+ if (l == 0)
+ return Size2(p_width, get_height());
+
+ float line_w = 0;
+ float h = 0;
+ float space_w = get_char_size(' ').width;
+ Vector<String> lines = p_string.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ h += get_height();
+ String t = lines[i];
+ line_w = 0;
+ Vector<String> words = t.split(" ");
+ for (int j = 0; j < words.size(); j++) {
+ line_w += get_string_size(words[j]).x;
+ if (line_w > p_width) {
+ h += get_height();
+ line_w = get_string_size(words[j]).x;
+ } else {
+ line_w += space_w;
+ }
+ }
+ }
+
+ return Size2(p_width, h);
+}
+
void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) {
- ERR_FAIL_COND(p_fallback == this);
+ for (Ref<BitmapFont> fallback_child = p_fallback; fallback_child != NULL; fallback_child = fallback_child->get_fallback()) {
+ if (fallback_child == this) {
+ ERR_EXPLAIN("Can't set as fallback one of its parents to prevent crashes due to recursive loop.");
+ ERR_FAIL_COND(fallback_child == this);
+ }
+ }
+
fallback = p_fallback;
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 6bc8d9b792..def2c2285a 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -53,6 +53,7 @@ public:
virtual Size2 get_char_size(CharType p_char, CharType p_next = 0) const = 0;
Size2 get_string_size(const String &p_string) const;
+ Size2 get_wordwrap_string_size(const String &p_string, float p_width) const;
virtual bool is_distance_field_hint() const = 0;
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index a51a0ca0d0..7e7fc99a5d 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -91,7 +91,7 @@ public:
int high = points.size() - 1;
int middle = 0;
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
if (low > high)
ERR_PRINT("low > high, this may be a bug");
#endif
diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp
new file mode 100644
index 0000000000..f763700d52
--- /dev/null
+++ b/scene/resources/height_map_shape.cpp
@@ -0,0 +1,205 @@
+/*************************************************************************/
+/* height_map_shape.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "height_map_shape.h"
+#include "servers/physics_server.h"
+
+Vector<Vector3> HeightMapShape::_gen_debug_mesh_lines() {
+ Vector<Vector3> points;
+
+ if ((map_width != 0) && (map_depth != 0)) {
+
+ // This will be slow for large maps...
+ // also we'll have to figure out how well bullet centers this shape...
+
+ Vector2 size(map_width - 1, map_depth - 1);
+ Vector2 start = size * -0.5;
+
+ PoolRealArray::Read r = map_data.read();
+
+ // reserve some memory for our points..
+ points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2));
+
+ // now set our points
+ int r_offset = 0;
+ int w_offset = 0;
+ for (int d = 0; d < map_depth; d++) {
+ Vector3 height(start.x, 0.0, start.y);
+
+ for (int w = 0; w < map_width; w++) {
+ height.y = r[r_offset++];
+
+ if (w != map_width - 1) {
+ points.write[w_offset++] = height;
+ points.write[w_offset++] = Vector3(height.x + 1.0, r[r_offset], height.z);
+ }
+
+ if (d != map_depth - 1) {
+ points.write[w_offset++] = height;
+ points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0);
+ }
+
+ height.x += 1.0;
+ }
+
+ start.y += 1.0;
+ }
+ }
+
+ return points;
+}
+
+void HeightMapShape::_update_shape() {
+
+ Dictionary d;
+ d["width"] = map_width;
+ d["depth"] = map_depth;
+ d["heights"] = map_data;
+ d["min_height"] = min_height;
+ d["max_height"] = max_height;
+ PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+ Shape::_update_shape();
+}
+
+void HeightMapShape::set_map_width(int p_new) {
+ if (p_new < 1) {
+ // ignore
+ } else if (map_width != p_new) {
+ int was_size = map_width * map_depth;
+ map_width = p_new;
+
+ int new_size = map_width * map_depth;
+ map_data.resize(map_width * map_depth);
+
+ PoolRealArray::Write w = map_data.write();
+ while (was_size < new_size) {
+ w[was_size++] = 0.0;
+ }
+
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("map_width");
+ _change_notify("map_data");
+ }
+}
+
+int HeightMapShape::get_map_width() const {
+ return map_width;
+}
+
+void HeightMapShape::set_map_depth(int p_new) {
+ if (p_new < 1) {
+ // ignore
+ } else if (map_depth != p_new) {
+ int was_size = map_width * map_depth;
+ map_depth = p_new;
+
+ int new_size = map_width * map_depth;
+ map_data.resize(new_size);
+
+ PoolRealArray::Write w = map_data.write();
+ while (was_size < new_size) {
+ w[was_size++] = 0.0;
+ }
+
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("map_depth");
+ _change_notify("map_data");
+ }
+}
+
+int HeightMapShape::get_map_depth() const {
+ return map_depth;
+}
+
+void HeightMapShape::set_map_data(PoolRealArray p_new) {
+ int size = (map_width * map_depth);
+ if (p_new.size() != size) {
+ // fail
+ return;
+ }
+
+ // copy
+ PoolRealArray::Write w = map_data.write();
+ PoolRealArray::Read r = p_new.read();
+ for (int i = 0; i < size; i++) {
+ float val = r[i];
+ w[i] = val;
+ if (i == 0) {
+ min_height = val;
+ max_height = val;
+ } else {
+ if (min_height > val)
+ min_height = val;
+
+ if (max_height < val)
+ max_height = val;
+ }
+ }
+
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("map_data");
+}
+
+PoolRealArray HeightMapShape::get_map_data() const {
+ return map_data;
+}
+
+void HeightMapShape::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape::set_map_width);
+ ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape::get_map_width);
+ ClassDB::bind_method(D_METHOD("set_map_depth", "height"), &HeightMapShape::set_map_depth);
+ ClassDB::bind_method(D_METHOD("get_map_depth"), &HeightMapShape::get_map_depth);
+ ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape::set_map_data);
+ ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape::get_map_data);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_width", "get_map_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_depth", "get_map_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "map_data"), "set_map_data", "get_map_data");
+}
+
+HeightMapShape::HeightMapShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_HEIGHTMAP)) {
+
+ map_width = 2;
+ map_depth = 2;
+ map_data.resize(map_width * map_depth);
+ PoolRealArray::Write w = map_data.write();
+ w[0] = 0.0;
+ w[1] = 0.0;
+ w[2] = 0.0;
+ w[3] = 0.0;
+ min_height = 0.0;
+ max_height = 0.0;
+
+ _update_shape();
+}
diff --git a/scene/resources/height_map_shape.h b/scene/resources/height_map_shape.h
new file mode 100644
index 0000000000..b062f4e893
--- /dev/null
+++ b/scene/resources/height_map_shape.h
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* height_map_shape.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef HEIGHT_MAP_SHAPE_H
+#define HEIGHT_MAP_SHAPE_H
+
+#include "scene/resources/shape.h"
+
+class HeightMapShape : public Shape {
+ GDCLASS(HeightMapShape, Shape);
+
+ int map_width;
+ int map_depth;
+ PoolRealArray map_data;
+ float min_height;
+ float max_height;
+
+protected:
+ static void _bind_methods();
+ virtual void _update_shape();
+
+ virtual Vector<Vector3> _gen_debug_mesh_lines();
+
+public:
+ void set_map_width(int p_new);
+ int get_map_width() const;
+ void set_map_depth(int p_new);
+ int get_map_depth() const;
+ void set_map_data(PoolRealArray p_new);
+ PoolRealArray get_map_data() const;
+
+ HeightMapShape();
+};
+
+#endif /* !HEIGHT_MAP_SHAPE_H */
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 536eb11a27..ada0ac07a3 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -30,11 +30,20 @@
#include "material.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif
+
#include "scene/scene_string_names.h"
void Material::set_next_pass(const Ref<Material> &p_pass) {
- ERR_FAIL_COND(p_pass == this);
+ for (Ref<Material> pass_child = p_pass; pass_child != NULL; pass_child = pass_child->get_next_pass()) {
+ if (pass_child == this) {
+ ERR_EXPLAIN("Can't set as next_pass one of its parents to prevent crashes due to recursive loop.");
+ ERR_FAIL_COND(pass_child == this);
+ }
+ }
if (next_pass == p_pass)
return;
@@ -236,6 +245,12 @@ void ShaderMaterial::_bind_methods() {
void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+#ifdef TOOLS_ENABLED
+ const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+#else
+ const String quote_style = "\"";
+#endif
+
String f = p_function.operator String();
if ((f == "get_shader_param" || f == "set_shader_param") && p_idx == 0) {
@@ -243,7 +258,7 @@ void ShaderMaterial::get_argument_options(const StringName &p_function, int p_id
List<PropertyInfo> pl;
shader->get_param_list(&pl);
for (List<PropertyInfo>::Element *E = pl.front(); E; E = E->next()) {
- r_options->push_back("\"" + E->get().name.replace_first("shader_param/", "") + "\"");
+ r_options->push_back(quote_style + E->get().name.replace_first("shader_param/", "") + quote_style);
}
}
}
@@ -458,6 +473,9 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_ENSURE_CORRECT_NORMALS]) {
code += ",ensure_correct_normals";
}
+ if (flags[FLAG_USE_SHADOW_TO_OPACITY]) {
+ code += ",shadow_to_opacity";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -839,7 +857,7 @@ void SpatialMaterial::_update_shader() {
code += "\tALBEDO *= 1.0 - ref_amount;\n";
code += "\tALPHA = 1.0;\n";
- } else if (features[FEATURE_TRANSPARENT] || flags[FLAG_USE_ALPHA_SCISSOR] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
+ } else if (features[FEATURE_TRANSPARENT] || flags[FLAG_USE_ALPHA_SCISSOR] || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
code += "\tALPHA = albedo.a * albedo_tex.a;\n";
}
@@ -1339,7 +1357,7 @@ void SpatialMaterial::set_flag(Flags p_flag, bool p_enabled) {
return;
flags[p_flag] = p_enabled;
- if (p_flag == FLAG_USE_ALPHA_SCISSOR || p_flag == FLAG_UNSHADED) {
+ if ((p_flag == FLAG_USE_ALPHA_SCISSOR) || (p_flag == FLAG_UNSHADED) || (p_flag == FLAG_USE_SHADOW_TO_OPACITY)) {
_change_notify();
}
_queue_shader_change();
@@ -2050,6 +2068,7 @@ void SpatialMaterial::_bind_methods() {
ADD_GROUP("Flags", "flags_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_transparent"), "set_feature", "get_feature", FEATURE_TRANSPARENT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_use_shadow_to_opacity"), "set_flag", "get_flag", FLAG_USE_SHADOW_TO_OPACITY);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_unshaded"), "set_flag", "get_flag", FLAG_UNSHADED);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_vertex_lighting"), "set_flag", "get_flag", FLAG_USE_VERTEX_LIGHTING);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST);
@@ -2256,6 +2275,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
BIND_ENUM_CONSTANT(FLAG_DISABLE_AMBIENT_LIGHT);
BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS);
+ BIND_ENUM_CONSTANT(FLAG_USE_SHADOW_TO_OPACITY);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 1f7fc267af..29b45f769b 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -196,6 +196,7 @@ public:
FLAG_DONT_RECEIVE_SHADOWS,
FLAG_ENSURE_CORRECT_NORMALS,
FLAG_DISABLE_AMBIENT_LIGHT,
+ FLAG_USE_SHADOW_TO_OPACITY,
FLAG_MAX
};
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 85018f38d7..6443b44bb6 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -37,6 +37,8 @@
#include <stdlib.h>
+Mesh::ConvexDecompositionFunc Mesh::convex_composition_function = NULL;
+
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
if (triangle_mesh.is_valid())
@@ -487,6 +489,7 @@ void Mesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count);
ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays);
ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &Mesh::surface_get_blend_shape_arrays);
+ ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &Mesh::surface_set_material);
ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &Mesh::surface_get_material);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
@@ -543,6 +546,49 @@ void Mesh::clear_cache() const {
debug_lines.clear();
}
+Vector<Ref<Shape> > Mesh::convex_decompose() const {
+
+ ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape> >());
+
+ PoolVector<Face3> faces = get_faces();
+ Vector<Face3> f3;
+ f3.resize(faces.size());
+ PoolVector<Face3>::Read f = faces.read();
+ for (int i = 0; i < f3.size(); i++) {
+ f3.write[i] = f[i];
+ }
+
+ Vector<Vector<Face3> > decomposed = convex_composition_function(f3);
+
+ Vector<Ref<Shape> > ret;
+
+ for (int i = 0; i < decomposed.size(); i++) {
+ Set<Vector3> points;
+ for (int j = 0; j < decomposed[i].size(); j++) {
+ points.insert(decomposed[i][j].vertex[0]);
+ points.insert(decomposed[i][j].vertex[1]);
+ points.insert(decomposed[i][j].vertex[2]);
+ }
+
+ PoolVector<Vector3> convex_points;
+ convex_points.resize(points.size());
+ {
+ PoolVector<Vector3>::Write w = convex_points.write();
+ int idx = 0;
+ for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) {
+ w[idx++] = E->get();
+ }
+ }
+
+ Ref<ConvexPolygonShape> shape;
+ shape.instance();
+ shape->set_points(convex_points);
+ ret.push_back(shape);
+ }
+
+ return ret;
+}
+
Mesh::Mesh() {
}
@@ -1260,7 +1306,6 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("surface_get_array_index_len", "surf_idx"), &ArrayMesh::surface_get_array_index_len);
ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format);
ClassDB::bind_method(D_METHOD("surface_get_primitive_type", "surf_idx"), &ArrayMesh::surface_get_primitive_type);
- ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &ArrayMesh::surface_set_material);
ClassDB::bind_method(D_METHOD("surface_find_by_name", "name"), &ArrayMesh::surface_find_by_name);
ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name);
ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name);
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index dabfc6ea60..b38791b9a6 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -31,6 +31,7 @@
#ifndef MESH_H
#define MESH_H
+#include "core/math/face3.h"
#include "core/math/triangle_mesh.h"
#include "core/resource.h"
#include "scene/resources/material.h"
@@ -127,6 +128,7 @@ public:
virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0;
virtual uint32_t surface_get_format(int p_idx) const = 0;
virtual PrimitiveType surface_get_primitive_type(int p_idx) const = 0;
+ virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) = 0;
virtual Ref<Material> surface_get_material(int p_idx) const = 0;
virtual int get_blend_shape_count() const = 0;
virtual StringName get_blend_shape_name(int p_index) const = 0;
@@ -147,6 +149,12 @@ public:
Size2 get_lightmap_size_hint() const;
void clear_cache() const;
+ typedef Vector<Vector<Face3> > (*ConvexDecompositionFunc)(const Vector<Face3> &);
+
+ static ConvexDecompositionFunc convex_composition_function;
+
+ Vector<Ref<Shape> > convex_decompose() const;
+
Mesh();
};
@@ -208,8 +216,8 @@ public:
PrimitiveType surface_get_primitive_type(int p_idx) const;
bool surface_is_alpha_sorting_enabled(int p_idx) const;
- void surface_set_material(int p_idx, const Ref<Material> &p_material);
- Ref<Material> surface_get_material(int p_idx) const;
+ virtual void surface_set_material(int p_idx, const Ref<Material> &p_material);
+ virtual Ref<Material> surface_get_material(int p_idx) const;
int surface_find_by_name(const String &p_name) const;
void surface_set_name(int p_idx, const String &p_name);
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index c5125d576d..d40a5dee2e 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -56,6 +56,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
set_item_preview(idx, p_value);
else if (what == "navmesh")
set_item_navmesh(idx, p_value);
+ else if (what == "navmesh_transform")
+ set_item_navmesh_transform(idx, p_value);
else
return false;
@@ -80,6 +82,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = _get_item_shapes(idx);
else if (what == "navmesh")
r_ret = get_item_navmesh(idx);
+ else if (what == "navmesh_transform")
+ r_ret = get_item_navmesh_transform(idx);
else if (what == "preview")
r_ret = get_item_preview(idx);
else
@@ -95,8 +99,10 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
String name = "item/" + itos(E->key()) + "/";
p_list->push_back(PropertyInfo(Variant::STRING, name + "name"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "mesh_transform"));
p_list->push_back(PropertyInfo(Variant::ARRAY, name + "shapes"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "navmesh_transform"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "preview", PROPERTY_HINT_RESOURCE_TYPE, "Texture", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER));
}
}
@@ -116,6 +122,7 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) {
emit_changed();
_change_notify();
}
+
void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
ERR_FAIL_COND(!item_map.has(p_item));
@@ -145,6 +152,15 @@ void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navm
_change_notify();
}
+void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform &p_transform) {
+
+ ERR_FAIL_COND(!item_map.has(p_item));
+ item_map[p_item].navmesh_transform = p_transform;
+ notify_change_to_owners();
+ emit_changed();
+ _change_notify();
+}
+
void MeshLibrary::set_item_preview(int p_item, const Ref<Texture> &p_preview) {
ERR_FAIL_COND(!item_map.has(p_item));
@@ -152,11 +168,13 @@ void MeshLibrary::set_item_preview(int p_item, const Ref<Texture> &p_preview) {
emit_changed();
_change_notify();
}
+
String MeshLibrary::get_item_name(int p_item) const {
ERR_FAIL_COND_V(!item_map.has(p_item), "");
return item_map[p_item].name;
}
+
Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const {
ERR_FAIL_COND_V(!item_map.has(p_item), Ref<Mesh>());
@@ -175,6 +193,12 @@ Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const {
return item_map[p_item].navmesh;
}
+Transform MeshLibrary::get_item_navmesh_transform(int p_item) const {
+
+ ERR_FAIL_COND_V(!item_map.has(p_item), Transform());
+ return item_map[p_item].navmesh_transform;
+}
+
Ref<Texture> MeshLibrary::get_item_preview(int p_item) const {
ERR_FAIL_COND_V(!item_map.has(p_item), Ref<Texture>());
@@ -268,11 +292,13 @@ void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh);
ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh);
+ ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform);
ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes);
ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview);
ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name);
ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh);
ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh);
+ ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform);
ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes);
ClassDB::bind_method(D_METHOD("get_item_preview", "id"), &MeshLibrary::get_item_preview);
ClassDB::bind_method(D_METHOD("remove_item", "id"), &MeshLibrary::remove_item);
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index 849a0577eb..4ae4fc6483 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -52,6 +52,7 @@ public:
Ref<Mesh> mesh;
Vector<ShapeData> shapes;
Ref<Texture> preview;
+ Transform navmesh_transform;
Ref<NavigationMesh> navmesh;
};
@@ -72,11 +73,13 @@ public:
void set_item_name(int p_item, const String &p_name);
void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh);
void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh);
+ void set_item_navmesh_transform(int p_item, const Transform &p_transform);
void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes);
void set_item_preview(int p_item, const Ref<Texture> &p_preview);
String get_item_name(int p_item) const;
Ref<Mesh> get_item_mesh(int p_item) const;
Ref<NavigationMesh> get_item_navmesh(int p_item) const;
+ Transform get_item_navmesh_transform(int p_item) const;
Vector<ShapeData> get_item_shapes(int p_item) const;
Ref<Texture> get_item_preview(int p_item) const;
diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp
index 1b406551ab..5d8adc0c8d 100644
--- a/scene/resources/multimesh.cpp
+++ b/scene/resources/multimesh.cpp
@@ -32,6 +32,8 @@
#include "servers/visual_server.h"
void MultiMesh::_set_transform_array(const PoolVector<Vector3> &p_array) {
+ if (transform_format != TRANSFORM_3D)
+ return;
PoolVector<Vector3> xforms = p_array;
int len = xforms.size();
@@ -55,6 +57,9 @@ void MultiMesh::_set_transform_array(const PoolVector<Vector3> &p_array) {
PoolVector<Vector3> MultiMesh::_get_transform_array() const {
+ if (transform_format != TRANSFORM_3D)
+ return PoolVector<Vector3>();
+
if (instance_count == 0)
return PoolVector<Vector3>();
@@ -75,6 +80,54 @@ PoolVector<Vector3> MultiMesh::_get_transform_array() const {
return xforms;
}
+void MultiMesh::_set_transform_2d_array(const PoolVector<Vector2> &p_array) {
+
+ if (transform_format != TRANSFORM_2D)
+ return;
+
+ PoolVector<Vector2> xforms = p_array;
+ int len = xforms.size();
+ ERR_FAIL_COND((len / 3) != instance_count);
+ if (len == 0)
+ return;
+
+ PoolVector<Vector2>::Read r = xforms.read();
+
+ for (int i = 0; i < len / 3; i++) {
+
+ Transform2D t;
+ t.elements[0] = r[i * 3 + 0];
+ t.elements[1] = r[i * 3 + 1];
+ t.elements[2] = r[i * 3 + 2];
+
+ set_instance_transform_2d(i, t);
+ }
+}
+
+PoolVector<Vector2> MultiMesh::_get_transform_2d_array() const {
+
+ if (transform_format != TRANSFORM_2D)
+ return PoolVector<Vector2>();
+
+ if (instance_count == 0)
+ return PoolVector<Vector2>();
+
+ PoolVector<Vector2> xforms;
+ xforms.resize(instance_count * 3);
+
+ PoolVector<Vector2>::Write w = xforms.write();
+
+ for (int i = 0; i < instance_count; i++) {
+
+ Transform2D t = get_instance_transform_2d(i);
+ w[i * 3 + 0] = t.elements[0];
+ w[i * 3 + 1] = t.elements[1];
+ w[i * 3 + 2] = t.elements[2];
+ }
+
+ return xforms;
+}
+
void MultiMesh::_set_color_array(const PoolVector<Color> &p_array) {
PoolVector<Color> colors = p_array;
@@ -162,6 +215,16 @@ int MultiMesh::get_instance_count() const {
return instance_count;
}
+void MultiMesh::set_visible_instance_count(int p_count) {
+ ERR_FAIL_COND(p_count < -1);
+ VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, p_count);
+ visible_instance_count = p_count;
+}
+int MultiMesh::get_visible_instance_count() const {
+
+ return visible_instance_count;
+}
+
void MultiMesh::set_instance_transform(int p_instance, const Transform &p_transform) {
VisualServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform);
@@ -200,6 +263,11 @@ Color MultiMesh::get_instance_custom_data(int p_instance) const {
return VisualServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance);
}
+void MultiMesh::set_as_bulk_array(const PoolVector<float> &p_array) {
+
+ VisualServer::get_singleton()->multimesh_set_as_bulk_array(multimesh, p_array);
+}
+
AABB MultiMesh::get_aabb() const {
return VisualServer::get_singleton()->multimesh_get_aabb(multimesh);
@@ -255,6 +323,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_instance_count", "count"), &MultiMesh::set_instance_count);
ClassDB::bind_method(D_METHOD("get_instance_count"), &MultiMesh::get_instance_count);
+ ClassDB::bind_method(D_METHOD("set_visible_instance_count", "count"), &MultiMesh::set_visible_instance_count);
+ ClassDB::bind_method(D_METHOD("get_visible_instance_count"), &MultiMesh::get_visible_instance_count);
ClassDB::bind_method(D_METHOD("set_instance_transform", "instance", "transform"), &MultiMesh::set_instance_transform);
ClassDB::bind_method(D_METHOD("set_instance_transform_2d", "instance", "transform"), &MultiMesh::set_instance_transform_2d);
ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform);
@@ -263,10 +333,13 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color);
ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data);
ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data);
+ ClassDB::bind_method(D_METHOD("set_as_bulk_array", "array"), &MultiMesh::set_as_bulk_array);
ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb);
ClassDB::bind_method(D_METHOD("_set_transform_array"), &MultiMesh::_set_transform_array);
ClassDB::bind_method(D_METHOD("_get_transform_array"), &MultiMesh::_get_transform_array);
+ ClassDB::bind_method(D_METHOD("_set_transform_2d_array"), &MultiMesh::_set_transform_2d_array);
+ ClassDB::bind_method(D_METHOD("_get_transform_2d_array"), &MultiMesh::_get_transform_2d_array);
ClassDB::bind_method(D_METHOD("_set_color_array"), &MultiMesh::_set_color_array);
ClassDB::bind_method(D_METHOD("_get_color_array"), &MultiMesh::_get_color_array);
ClassDB::bind_method(D_METHOD("_set_custom_data_array"), &MultiMesh::_set_custom_data_array);
@@ -276,8 +349,10 @@ void MultiMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_format", PROPERTY_HINT_ENUM, "None,Byte,Float"), "set_custom_data_format", "get_custom_data_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_array", "_get_transform_array");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "transform_2d_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_2d_array", "_get_transform_2d_array");
ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_color_array", "_get_color_array");
ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_custom_data_array", "_get_custom_data_array");
@@ -299,6 +374,7 @@ MultiMesh::MultiMesh() {
color_format = COLOR_NONE;
custom_data_format = CUSTOM_DATA_NONE;
transform_format = TRANSFORM_2D;
+ visible_instance_count = -1;
instance_count = 0;
}
diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h
index ac2c69e022..6b17e9278d 100644
--- a/scene/resources/multimesh.h
+++ b/scene/resources/multimesh.h
@@ -64,6 +64,7 @@ private:
ColorFormat color_format;
CustomDataFormat custom_data_format;
int instance_count;
+ int visible_instance_count;
protected:
static void _bind_methods();
@@ -71,6 +72,9 @@ protected:
void _set_transform_array(const PoolVector<Vector3> &p_array);
PoolVector<Vector3> _get_transform_array() const;
+ void _set_transform_2d_array(const PoolVector<Vector2> &p_array);
+ PoolVector<Vector2> _get_transform_2d_array() const;
+
void _set_color_array(const PoolVector<Color> &p_array);
PoolVector<Color> _get_color_array() const;
@@ -93,6 +97,9 @@ public:
void set_instance_count(int p_count);
int get_instance_count() const;
+ void set_visible_instance_count(int p_count);
+ int get_visible_instance_count() const;
+
void set_instance_transform(int p_instance, const Transform &p_transform);
void set_instance_transform_2d(int p_instance, const Transform2D &p_transform);
Transform get_instance_transform(int p_instance) const;
@@ -104,6 +111,8 @@ public:
void set_instance_custom_data(int p_instance, const Color &p_custom_data);
Color get_instance_custom_data(int p_instance) const;
+ void set_as_bulk_array(const PoolVector<float> &p_array);
+
virtual AABB get_aabb() const;
virtual RID get_rid() const;
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 626ed9f5b4..2c6f30f429 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -535,7 +535,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
float a = value;
float b = original;
- if (Math::abs(a - b) < CMP_EPSILON)
+ if (Math::is_equal_approx(a, b))
continue;
} else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) {
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index ef67e6ea80..758475b75e 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -1186,6 +1186,7 @@ void ParticlesMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_Z);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp
index 419ff8f972..08f6ccd764 100644
--- a/scene/resources/plane_shape.cpp
+++ b/scene/resources/plane_shape.cpp
@@ -64,6 +64,7 @@ Vector<Vector3> PlaneShape::_gen_debug_mesh_lines() {
void PlaneShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), plane);
+ Shape::_update_shape();
}
void PlaneShape::set_plane(Plane p_plane) {
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index db58fe7823..04d13c8869 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -157,6 +157,12 @@ Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {
return primitive_type;
}
+void PrimitiveMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
+ ERR_FAIL_INDEX(p_idx, 1);
+
+ set_material(p_material);
+}
+
Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, 1, NULL);
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 88a26801b7..0045a48736 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -72,6 +72,7 @@ public:
virtual Array surface_get_blend_shape_arrays(int p_surface) const;
virtual uint32_t surface_get_format(int p_idx) const;
virtual Mesh::PrimitiveType surface_get_primitive_type(int p_idx) const;
+ virtual void surface_set_material(int p_idx, const Ref<Material> &p_material);
virtual Ref<Material> surface_get_material(int p_idx) const;
virtual int get_blend_shape_count() const;
virtual StringName get_blend_shape_name(int p_index) const;
diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp
index b7925d8a58..0acfffdc06 100644
--- a/scene/resources/ray_shape.cpp
+++ b/scene/resources/ray_shape.cpp
@@ -47,7 +47,7 @@ void RayShape::_update_shape() {
d["length"] = length;
d["slips_on_slope"] = slips_on_slope;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
- emit_changed();
+ Shape::_update_shape();
}
void RayShape::set_length(float p_length) {
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 210fbe9f20..0921c0dae9 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -445,6 +445,10 @@ Error ResourceInteractiveLoaderText::poll() {
} else {
resource_cache.push_back(res);
+#ifdef TOOLS_ENABLED
+ //remember ID for saving
+ res->set_id_for_path(local_path, index);
+#endif
}
ExtResource er;
@@ -1355,7 +1359,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) {
if (external_resources.has(res)) {
- return "ExtResource( " + itos(external_resources[res] + 1) + " )";
+ return "ExtResource( " + itos(external_resources[res]) + " )";
} else {
if (internal_resources.has(res)) {
@@ -1459,7 +1463,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
_find_resources(v);
}
} break;
- default: {}
+ default: {
+ }
}
}
@@ -1538,18 +1543,61 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_line("]\n"); //one empty line
}
- Vector<RES> sorted_er;
- sorted_er.resize(external_resources.size());
+ {
+ }
+
+#ifdef TOOLS_ENABLED
+ //keep order from cached ids
+ Set<int> cached_ids_found;
+ for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
+ int cached_id = E->key()->get_id_for_path(local_path);
+ if (cached_id < 0 || cached_ids_found.has(cached_id)) {
+ E->get() = -1; //reset
+ } else {
+ E->get() = cached_id;
+ cached_ids_found.insert(cached_id);
+ }
+ }
+ //create IDs for non cached resources
+ for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
+ if (cached_ids_found.has(E->get())) { //already cached, go on
+ continue;
+ }
+
+ int attempt = 1; //start from one, more readable format
+ while (cached_ids_found.has(attempt)) {
+ attempt++;
+ }
+
+ cached_ids_found.insert(attempt);
+ E->get() = attempt;
+ //update also in resource
+ Ref<Resource> res = E->key();
+ res->set_id_for_path(local_path, attempt);
+ }
+#else
+ //make sure to start from one, as it makes format more readable
+ for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
+ E->get() = E->get() + 1;
+ }
+#endif
+
+ Vector<ResourceSort> sorted_er;
for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
- sorted_er.write[E->get()] = E->key();
+ ResourceSort rs;
+ rs.resource = E->key();
+ rs.index = E->get();
+ sorted_er.push_back(rs);
}
+ sorted_er.sort();
+
for (int i = 0; i < sorted_er.size(); i++) {
- String p = sorted_er[i]->get_path();
+ String p = sorted_er[i].resource->get_path();
- f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i]->get_save_class() + "\" id=" + itos(i + 1) + "]\n"); //bundled
+ f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i].resource->get_save_class() + "\" id=" + itos(sorted_er[i].index) + "]\n"); //bundled
}
if (external_resources.size())
@@ -1645,7 +1693,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
}
- f->store_string("\n");
+ if (E->next())
+ f->store_line(String());
}
if (packed_scene.is_valid()) {
@@ -1663,15 +1712,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
Vector<StringName> groups = state->get_node_groups(i);
String header = "[node";
- header += " name=\"" + String(name) + "\"";
+ header += " name=\"" + String(name).c_escape() + "\"";
if (type != StringName()) {
header += " type=\"" + String(type) + "\"";
}
if (path != NodePath()) {
- header += " parent=\"" + String(path.simplified()) + "\"";
+ header += " parent=\"" + String(path.simplified()).c_escape() + "\"";
}
if (owner != NodePath() && owner != NodePath(".")) {
- header += " owner=\"" + String(owner.simplified()) + "\"";
+ header += " owner=\"" + String(owner.simplified()).c_escape() + "\"";
}
if (index >= 0) {
header += " index=\"" + itos(index) + "\"";
@@ -1714,7 +1763,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n");
}
- f->store_line(String());
+ if (i < state->get_node_count() - 1)
+ f->store_line(String());
}
for (int i = 0; i < state->get_connection_count(); i++) {
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 8d78ab33b0..06c841229b 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -169,6 +169,14 @@ class ResourceFormatSaverTextInstance {
Map<RES, int> external_resources;
Map<RES, int> internal_resources;
+ struct ResourceSort {
+ RES resource;
+ int index;
+ bool operator<(const ResourceSort &p_right) const {
+ return index < p_right.index;
+ }
+ };
+
void _find_resources(const Variant &p_variant, bool p_main = false);
static String _write_resources(void *ud, const RES &p_resource);
diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp
index 825b7f4c8b..6ba46f066c 100644
--- a/scene/resources/shape.cpp
+++ b/scene/resources/shape.cpp
@@ -96,6 +96,11 @@ Ref<ArrayMesh> Shape::get_debug_mesh() {
return debug_mesh_cache;
}
+void Shape::_update_shape() {
+ emit_changed();
+ debug_mesh_cache.unref();
+}
+
void Shape::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape::set_margin);
diff --git a/scene/resources/shape.h b/scene/resources/shape.h
index de99a967a8..ba763eaab1 100644
--- a/scene/resources/shape.h
+++ b/scene/resources/shape.h
@@ -51,6 +51,8 @@ protected:
Shape(RID p_shape);
virtual Vector<Vector3> _gen_debug_mesh_lines() = 0; // { return Vector<Vector3>(); }
+ virtual void _update_shape();
+
public:
virtual RID get_rid() const { return shape; }
diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp
index f9f8ff19e4..292fdcdbd2 100644
--- a/scene/resources/sky.cpp
+++ b/scene/resources/sky.cpp
@@ -62,7 +62,7 @@ void Sky::_bind_methods() {
}
Sky::Sky() {
- radiance_size = RADIANCE_SIZE_512;
+ radiance_size = RADIANCE_SIZE_128;
}
/////////////////////////////////////////
@@ -553,10 +553,10 @@ ProceduralSky::ProceduralSky(bool p_desaturate) {
ground_energy = 1;
if (p_desaturate) {
- sky_top_color.set_hsv(sky_top_color.get_h(),0,sky_top_color.get_v());
- sky_horizon_color.set_hsv(sky_horizon_color.get_h(),0,sky_horizon_color.get_v());
- ground_bottom_color.set_hsv(ground_bottom_color.get_h(),0,ground_bottom_color.get_v());
- ground_horizon_color.set_hsv(ground_horizon_color.get_h(),0,ground_horizon_color.get_v());
+ sky_top_color.set_hsv(sky_top_color.get_h(), 0, sky_top_color.get_v());
+ sky_horizon_color.set_hsv(sky_horizon_color.get_h(), 0, sky_horizon_color.get_v());
+ ground_bottom_color.set_hsv(ground_bottom_color.get_h(), 0, ground_bottom_color.get_v());
+ ground_horizon_color.set_hsv(ground_horizon_color.get_h(), 0, ground_horizon_color.get_v());
}
sun_color = Color(1, 1, 1);
sun_latitude = 35;
diff --git a/scene/resources/sky.h b/scene/resources/sky.h
index 7327b2a627..3a6f44e11b 100644
--- a/scene/resources/sky.h
+++ b/scene/resources/sky.h
@@ -191,7 +191,7 @@ public:
virtual RID get_rid() const;
- ProceduralSky(bool p_desaturate=false);
+ ProceduralSky(bool p_desaturate = false);
~ProceduralSky();
};
diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp
index 492cf3959d..af89413ced 100644
--- a/scene/resources/sphere_shape.cpp
+++ b/scene/resources/sphere_shape.cpp
@@ -58,6 +58,7 @@ Vector<Vector3> SphereShape::_gen_debug_mesh_lines() {
void SphereShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), radius);
+ Shape::_update_shape();
}
void SphereShape::set_radius(float p_radius) {
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index cdd65c7642..5dd429fa75 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -368,25 +368,14 @@ Color StyleBoxFlat::get_bg_color() const {
return bg_color;
}
-void StyleBoxFlat::set_border_color_all(const Color &p_color) {
- for (int i = 0; i < 4; i++) {
+void StyleBoxFlat::set_border_color(const Color &p_color) {
- border_color.write()[i] = p_color;
- }
+ border_color = p_color;
emit_changed();
}
-Color StyleBoxFlat::get_border_color_all() const {
+Color StyleBoxFlat::get_border_color() const {
- return border_color[MARGIN_TOP];
-}
-void StyleBoxFlat::set_border_color(Margin p_border, const Color &p_color) {
-
- border_color.write()[p_border] = p_color;
- emit_changed();
-}
-Color StyleBoxFlat::get_border_color(Margin p_border) const {
-
- return border_color[p_border];
+ return border_color;
}
void StyleBoxFlat::set_border_width_all(int p_size) {
@@ -511,6 +500,16 @@ int StyleBoxFlat::get_shadow_size() const {
return shadow_size;
}
+void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) {
+
+ shadow_offset = p_offset;
+ emit_changed();
+}
+Point2 StyleBoxFlat::get_shadow_offset() const {
+
+ return shadow_offset;
+}
+
void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) {
anti_aliased = p_anti_aliased;
emit_changed();
@@ -552,7 +551,7 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re
inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0);
//tr
- rad = MIN(border_top, border_bottom);
+ rad = MIN(border_top, border_right);
inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0);
//br
@@ -565,12 +564,13 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re
}
inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 style_rect, const int corner_radius[4],
- const Rect2 ring_rect, const int border_width[4], const Color inner_color[4], const Color outer_color[4], const int corner_detail) {
+ const Rect2 ring_rect, const int border_width[4], const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) {
int vert_offset = verts.size();
if (!vert_offset) {
vert_offset = 0;
}
+
int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
int ring_corner_radius[4];
@@ -585,10 +585,11 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
Rect2 inner_rect;
inner_rect = ring_rect.grow_individual(-border_width[MARGIN_LEFT], -border_width[MARGIN_TOP], -border_width[MARGIN_RIGHT], -border_width[MARGIN_BOTTOM]);
+
int inner_corner_radius[4];
+ set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
Vector<Point2> inner_points;
- set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
inner_points.push_back(inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0])); //tl
inner_points.push_back(Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1])); //tr
inner_points.push_back(inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2])); //br
@@ -603,11 +604,11 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
Point2 corner_point;
if (inner_outer == 0) {
radius = inner_corner_radius[corner_index];
- color = *inner_color;
+ color = inner_color;
corner_point = inner_points[corner_index];
} else {
radius = ring_corner_radius[corner_index];
- color = *outer_color;
+ color = outer_color;
corner_point = outer_points[corner_index];
}
float x = radius * (float)cos((double)corner_index * Math_PI / 2.0 + (double)detail / (double)adapted_corner_detail * Math_PI / 2.0 + Math_PI) + corner_point.x;
@@ -618,17 +619,28 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
}
}
- int vert_count = (adapted_corner_detail + 1) * 4 * 2;
+ int ring_vert_count = verts.size() - vert_offset;
+
//fill the indices and the colors for the border
- for (int i = 0; i < vert_count; i++) {
- //poly 1
- indices.push_back(vert_offset + ((i + 0) % vert_count));
- indices.push_back(vert_offset + ((i + 2) % vert_count));
- indices.push_back(vert_offset + ((i + 1) % vert_count));
- //poly 2
- indices.push_back(vert_offset + ((i + 1) % vert_count));
- indices.push_back(vert_offset + ((i + 2) % vert_count));
- indices.push_back(vert_offset + ((i + 3) % vert_count));
+ for (int i = 0; i < ring_vert_count; i++) {
+ indices.push_back(vert_offset + ((i + 0) % ring_vert_count));
+ indices.push_back(vert_offset + ((i + 2) % ring_vert_count));
+ indices.push_back(vert_offset + ((i + 1) % ring_vert_count));
+ }
+
+ if (fill_center) {
+ //fill the indices and the colors for the center
+ for (int index = 0; index < ring_vert_count / 2; index += 2) {
+ int i = index;
+ //poly 1
+ indices.push_back(vert_offset + i);
+ indices.push_back(vert_offset + ring_vert_count - 4 - i);
+ indices.push_back(vert_offset + i + 2);
+ //poly 2
+ indices.push_back(vert_offset + i);
+ indices.push_back(vert_offset + ring_vert_count - 2 - i);
+ indices.push_back(vert_offset + ring_vert_count - 4 - i);
+ }
}
}
@@ -657,13 +669,29 @@ inline void adapt_values(int p_index_a, int p_index_b, int *adapted_values, cons
void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
//PREPARATIONS
+ bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0);
+ bool draw_shadow = (shadow_size > 0);
+ if (!draw_border && !draw_center && !draw_shadow) {
+ return;
+ }
bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
bool aa_on = rounded_corners && anti_aliased;
+ Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0);
+
+ bool blend_on = blend_border && draw_border;
+
Rect2 style_rect = p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]);
- if (aa_on) {
- style_rect = style_rect.grow(-((aa_size + 1) / 2));
+ Rect2 border_style_rect = style_rect;
+ if (aa_on && !blend_on) {
+ float aa_size_grow = 0.5 * ((aa_size + 1) / 2);
+ style_rect = style_rect.grow(-aa_size_grow);
+ for (int i = 0; i < 4; i++) {
+ if (border_width[i] > 0) {
+ border_style_rect = border_style_rect.grow_margin((Margin)i, -aa_size_grow);
+ }
+ }
}
//adapt borders (prevent weird overlapping/glitchy drawings)
@@ -686,79 +714,93 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Vector<int> indices;
Vector<Color> colors;
- //DRAWING
- VisualServer *vs = VisualServer::get_singleton();
-
//DRAW SHADOW
- if (shadow_size > 0) {
+ if (draw_shadow) {
int shadow_width[4] = { shadow_size, shadow_size, shadow_size, shadow_size };
- Color shadow_colors[4] = { shadow_color, shadow_color, shadow_color, shadow_color };
- Color shadow_colors_transparent[4];
- for (int i = 0; i < 4; i++) {
- shadow_colors_transparent[i] = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0);
+
+ Rect2 shadow_inner_rect = style_rect;
+ shadow_inner_rect.position += shadow_offset;
+
+ Rect2 shadow_rect = style_rect.grow(shadow_size);
+ shadow_rect.position += shadow_offset;
+
+ Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0);
+
+ draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner,
+ shadow_rect, shadow_width, shadow_color, shadow_color_transparent, corner_detail);
+
+ if (draw_center) {
+ int no_border[4] = { 0, 0, 0, 0 };
+ draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner,
+ shadow_inner_rect, no_border, shadow_color, shadow_color, corner_detail, true);
}
- draw_ring(verts, indices, colors, style_rect, adapted_corner,
- style_rect.grow(shadow_size), shadow_width, shadow_colors, shadow_colors_transparent, corner_detail);
}
//DRAW border
- Color bg_color_array[4] = { bg_color, bg_color, bg_color, bg_color };
- const Color *inner_color = ((blend_border) ? bg_color_array : border_color.read().ptr());
- draw_ring(verts, indices, colors, style_rect, adapted_corner,
- style_rect, adapted_border, inner_color, border_color.read().ptr(), corner_detail);
+ if (draw_border) {
+ draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
+ border_style_rect, adapted_border, blend_on ? (draw_center ? bg_color : border_color_alpha) : border_color, border_color, corner_detail);
+ }
//DRAW INFILL
if (draw_center) {
- int temp_vert_offset = verts.size();
int no_border[4] = { 0, 0, 0, 0 };
draw_ring(verts, indices, colors, style_rect, adapted_corner,
- infill_rect, no_border, &bg_color, &bg_color, corner_detail);
- int added_vert_count = verts.size() - temp_vert_offset;
- //fill the indices and the colors for the center
- for (int index = 0; index <= added_vert_count / 2; index += 2) {
- int i = index;
- //poly 1
- indices.push_back(temp_vert_offset + i);
- indices.push_back(temp_vert_offset + added_vert_count - 4 - i);
- indices.push_back(temp_vert_offset + i + 2);
- //poly 1
- indices.push_back(temp_vert_offset + i);
- indices.push_back(temp_vert_offset + added_vert_count - 2 - i);
- indices.push_back(temp_vert_offset + added_vert_count - 4 - i);
- }
+ infill_rect, no_border, bg_color, bg_color, corner_detail, true);
}
if (aa_on) {
-
- //HELPER ARRAYS
- Color border_color_alpha[4];
- for (int i = 0; i < 4; i++) {
- Color c = border_color.read().ptr()[i];
- border_color_alpha[i] = Color(c.r, c.g, c.b, 0);
+ Rect2 border_inner_rect = infill_rect;
+ int aa_border_width[4];
+ int aa_fill_width[4];
+ if (draw_border) {
+ border_inner_rect = border_style_rect.grow_individual(-adapted_border[MARGIN_LEFT], -adapted_border[MARGIN_TOP], -adapted_border[MARGIN_RIGHT], -adapted_border[MARGIN_BOTTOM]);
+ for (int i = 0; i < 4; i++) {
+ if (border_width[i] > 0) {
+ aa_border_width[i] = aa_size;
+ aa_fill_width[i] = 0;
+ } else {
+ aa_border_width[i] = 0;
+ aa_fill_width[i] = aa_size;
+ }
+ }
+ } else {
+ for (int i = 0; i < 4; i++) {
+ aa_fill_width[i] = aa_size;
+ }
}
- Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0);
- Color bg_color_array_alpha[4] = { alpha_bg, alpha_bg, alpha_bg, alpha_bg };
-
- int aa_border_width[4] = { aa_size, aa_size, aa_size, aa_size };
if (draw_center) {
- if (!blend_border) {
+ if (!draw_border || !blend_on) {
+ Rect2 aa_rect = infill_rect.grow_individual(aa_fill_width[MARGIN_LEFT], aa_fill_width[MARGIN_TOP],
+ aa_fill_width[MARGIN_RIGHT], aa_fill_width[MARGIN_BOTTOM]);
+
+ Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0);
+
//INFILL AA
draw_ring(verts, indices, colors, style_rect, adapted_corner,
- infill_rect.grow(aa_size), aa_border_width, bg_color_array, bg_color_array_alpha, corner_detail);
+ aa_rect, aa_fill_width, bg_color, alpha_bg, corner_detail);
}
- } else if (!(border_width[0] == 0 && border_width[1] == 0 && border_width[2] == 0 && border_width[3] == 0)) {
- //DRAW INNER BORDER AA
- draw_ring(verts, indices, colors, style_rect, adapted_corner,
- infill_rect, aa_border_width, border_color_alpha, border_color.read().ptr(), corner_detail);
}
- //DRAW OUTER BORDER AA
- if (!(border_width[0] == 0 && border_width[1] == 0 && border_width[2] == 0 && border_width[3] == 0)) {
- draw_ring(verts, indices, colors, style_rect, adapted_corner,
- style_rect.grow(aa_size), aa_border_width, border_color.read().ptr(), border_color_alpha, corner_detail);
+
+ if (draw_border) {
+ if (!blend_on) {
+ //DRAW INNER BORDER AA
+ draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
+ border_inner_rect, aa_border_width, border_color_alpha, border_color, corner_detail);
+ }
+
+ Rect2 aa_rect = border_style_rect.grow_individual(aa_border_width[MARGIN_LEFT], aa_border_width[MARGIN_TOP],
+ aa_border_width[MARGIN_RIGHT], aa_border_width[MARGIN_BOTTOM]);
+
+ //DRAW OUTER BORDER AA
+ draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
+ aa_rect, aa_border_width, border_color, border_color_alpha, corner_detail);
}
}
+ //DRAWING
+ VisualServer *vs = VisualServer::get_singleton();
vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors);
}
@@ -770,8 +812,8 @@ 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);
- ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color_all);
- ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color_all);
+ ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color);
+ ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color);
ClassDB::bind_method(D_METHOD("set_border_width_all", "width"), &StyleBoxFlat::set_border_width_all);
ClassDB::bind_method(D_METHOD("get_border_width_min"), &StyleBoxFlat::get_border_width_min);
@@ -802,6 +844,9 @@ void StyleBoxFlat::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &StyleBoxFlat::set_shadow_size);
ClassDB::bind_method(D_METHOD("get_shadow_size"), &StyleBoxFlat::get_shadow_size);
+ ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &StyleBoxFlat::set_shadow_offset);
+ ClassDB::bind_method(D_METHOD("get_shadow_offset"), &StyleBoxFlat::get_shadow_offset);
+
ClassDB::bind_method(D_METHOD("set_anti_aliased", "anti_aliased"), &StyleBoxFlat::set_anti_aliased);
ClassDB::bind_method(D_METHOD("is_anti_aliased"), &StyleBoxFlat::is_anti_aliased);
@@ -843,6 +888,7 @@ void StyleBoxFlat::_bind_methods() {
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size"), "set_shadow_size", "get_shadow_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset"), "set_shadow_offset", "get_shadow_offset");
ADD_GROUP("Anti Aliasing", "anti_aliasing_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased");
@@ -853,17 +899,14 @@ StyleBoxFlat::StyleBoxFlat() {
bg_color = Color(0.6, 0.6, 0.6);
shadow_color = Color(0, 0, 0, 0.6);
-
- border_color.append(Color(0.8, 0.8, 0.8));
- border_color.append(Color(0.8, 0.8, 0.8));
- border_color.append(Color(0.8, 0.8, 0.8));
- border_color.append(Color(0.8, 0.8, 0.8));
+ border_color = Color(0.8, 0.8, 0.8);
blend_border = false;
draw_center = true;
anti_aliased = true;
shadow_size = 0;
+ shadow_offset = Point2(0, 0);
corner_detail = 8;
aa_size = 1;
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 9062270765..c3965fe076 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -149,7 +149,7 @@ class StyleBoxFlat : public StyleBox {
Color bg_color;
Color shadow_color;
- PoolVector<Color> border_color;
+ Color border_color;
int border_width[4];
int expand_margin[4];
@@ -161,6 +161,7 @@ class StyleBoxFlat : public StyleBox {
int corner_detail;
int shadow_size;
+ Point2 shadow_offset;
int aa_size;
protected:
@@ -173,10 +174,8 @@ public:
Color get_bg_color() const;
//Border Color
- void set_border_color_all(const Color &p_color);
- Color get_border_color_all() const;
- void set_border_color(Margin p_border, const Color &p_color);
- Color get_border_color(Margin p_border) const;
+ void set_border_color(const Color &p_color);
+ Color get_border_color() const;
//BORDER
//width
@@ -218,6 +217,9 @@ public:
void set_shadow_size(const int &p_size);
int get_shadow_size() const;
+ void set_shadow_offset(const Point2 &p_offset);
+ Point2 get_shadow_offset() const;
+
//ANTI_ALIASING
void set_anti_aliased(const bool &p_anti_aliased);
bool is_anti_aliased() const;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 83f29503fa..496b1b2bdc 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -108,8 +108,55 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
vtx.bones = last_bones;
vtx.tangent = last_tangent.normal;
vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d;
+
+ const int expected_vertices = 4;
+
+ if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) {
+ //ensure vertices are the expected amount
+ ERR_FAIL_COND(vtx.weights.size() != vtx.bones.size());
+ if (vtx.weights.size() < expected_vertices) {
+ //less than required, fill
+ for (int i = vtx.weights.size(); i < expected_vertices; i++) {
+ vtx.weights.push_back(0);
+ vtx.bones.push_back(0);
+ }
+ } else if (vtx.weights.size() > expected_vertices) {
+ //more than required, sort, cap and normalize.
+ Vector<WeightSort> weights;
+ for (int i = 0; i < vtx.weights.size(); i++) {
+ WeightSort ws;
+ ws.index = vtx.bones[i];
+ ws.weight = vtx.weights[i];
+ weights.push_back(ws);
+ }
+
+ //sort
+ weights.sort();
+ //cap
+ weights.resize(expected_vertices);
+ //renormalize
+ float total = 0;
+ for (int i = 0; i < expected_vertices; i++) {
+ total += weights[i].weight;
+ }
+
+ vtx.weights.resize(expected_vertices);
+ vtx.bones.resize(expected_vertices);
+
+ for (int i = 0; i < expected_vertices; i++) {
+ if (total > 0) {
+ vtx.weights.write[i] = weights[i].weight / total;
+ } else {
+ vtx.weights.write[i] = 0;
+ }
+ vtx.bones.write[i] = weights[i].index;
+ }
+ }
+ }
+
vertex_array.push_back(vtx);
first = false;
+
format |= Mesh::ARRAY_FORMAT_VERTEX;
}
void SurfaceTool::add_color(Color p_color) {
@@ -161,7 +208,6 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) {
void SurfaceTool::add_bones(const Vector<int> &p_bones) {
ERR_FAIL_COND(!begun);
- ERR_FAIL_COND(p_bones.size() != 4);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES));
format |= Mesh::ARRAY_FORMAT_BONES;
@@ -171,8 +217,6 @@ void SurfaceTool::add_bones(const Vector<int> &p_bones) {
void SurfaceTool::add_weights(const Vector<float> &p_weights) {
ERR_FAIL_COND(!begun);
-
- ERR_FAIL_COND(p_weights.size() != 4);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS));
format |= Mesh::ARRAY_FORMAT_WEIGHTS;
@@ -397,7 +441,8 @@ Array SurfaceTool::commit_to_arrays() {
a[i] = array;
} break;
- default: {}
+ default: {
+ }
}
}
@@ -724,6 +769,26 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
material = p_existing->surface_get_material(p_surface);
}
+void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String p_blend_shape_name) {
+ clear();
+ primitive = p_existing->surface_get_primitive_type(p_surface);
+ Array arr = p_existing->surface_get_blend_shape_arrays(p_surface);
+ Array blend_shape_names;
+ int32_t shape_idx = -1;
+ for (int32_t i = 0; i < p_existing->get_blend_shape_count(); i++) {
+ String name = p_existing->get_blend_shape_name(i);
+ if (name == p_blend_shape_name) {
+ shape_idx = i;
+ break;
+ }
+ }
+ ERR_FAIL_COND(shape_idx == -1);
+ ERR_FAIL_COND(shape_idx >= arr.size());
+ Array mesh = arr[shape_idx];
+ ERR_FAIL_COND(mesh.size() != VS::ARRAY_MAX);
+ _create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format);
+}
+
void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) {
if (vertex_array.size() == 0) {
@@ -1026,8 +1091,10 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear);
ClassDB::bind_method(D_METHOD("create_from", "existing", "surface"), &SurfaceTool::create_from);
+ ClassDB::bind_method(D_METHOD("create_from_blend_shape", "existing", "surface", "blend_shape"), &SurfaceTool::create_from_blend_shape);
ClassDB::bind_method(D_METHOD("append_from", "existing", "surface", "transform"), &SurfaceTool::append_from);
ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT));
+ ClassDB::bind_method(D_METHOD("commit_to_arrays"), &SurfaceTool::commit_to_arrays);
}
SurfaceTool::SurfaceTool() {
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index a3b110f0d8..c4c71dca13 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -62,6 +62,14 @@ private:
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
};
+ struct WeightSort {
+ int index;
+ float weight;
+ bool operator<(const WeightSort &p_right) const {
+ return weight < p_right.weight;
+ }
+ };
+
bool begun;
bool first;
Mesh::PrimitiveType primitive;
@@ -128,6 +136,7 @@ public:
static Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays);
Array commit_to_arrays();
void create_from(const Ref<Mesh> &p_existing, int p_surface);
+ void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String p_blend_shape_name);
void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform);
Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 3ca2b56d08..92172912c2 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -34,6 +34,7 @@
#include "core/io/image_loader.h"
#include "core/method_bind_ext.gen.inc"
#include "core/os/os.h"
+#include "mesh.h"
#include "scene/resources/bit_map.h"
Size2 Texture::get_size() const {
@@ -110,10 +111,12 @@ void ImageTexture::reload_from_file() {
Ref<Image> img;
img.instance();
- Error err = ImageLoader::load_image(path, img);
- ERR_FAIL_COND(err != OK);
-
- create_from_image(img, flags);
+ if (ImageLoader::load_image(path, img) == OK) {
+ create_from_image(img, flags);
+ } else {
+ Resource::reload_from_file();
+ _change_notify();
+ }
}
bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) {
@@ -637,7 +640,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_
bool mipmaps = df & FORMAT_BIT_HAS_MIPMAPS;
if (!mipmaps) {
- int size = Image::get_image_data_size(tw, th, format, 0);
+ int size = Image::get_image_data_size(tw, th, format, false);
PoolVector<uint8_t> img_data;
img_data.resize(size);
@@ -659,7 +662,6 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_
int mipmaps2 = Image::get_image_required_mipmaps(tw, th, format);
int total_size = Image::get_image_data_size(tw, th, format, true);
int idx = 0;
- int ofs = 0;
while (mipmaps2 > 1 && p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {
@@ -669,9 +671,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_
idx++;
}
- if (idx > 0) {
- ofs = Image::get_image_data_size(tw, th, format, idx - 1);
- }
+ int ofs = Image::get_image_mipmap_offset(tw, th, format, idx);
if (total_size - ofs <= 0) {
memdelete(f);
@@ -1133,6 +1133,138 @@ AtlasTexture::AtlasTexture() {
filter_clip = false;
}
+/////////////////////////////////////////
+
+int MeshTexture::get_width() const {
+ return size.width;
+}
+int MeshTexture::get_height() const {
+ return size.height;
+}
+RID MeshTexture::get_rid() const {
+ return RID();
+}
+
+bool MeshTexture::has_alpha() const {
+ return false;
+}
+
+void MeshTexture::set_flags(uint32_t p_flags) {
+}
+
+uint32_t MeshTexture::get_flags() const {
+ return 0;
+}
+
+void MeshTexture::set_mesh(const Ref<Mesh> &p_mesh) {
+ mesh = p_mesh;
+}
+Ref<Mesh> MeshTexture::get_mesh() const {
+ return mesh;
+}
+
+void MeshTexture::set_image_size(const Size2 &p_size) {
+ size = p_size;
+}
+
+Size2 MeshTexture::get_image_size() const {
+
+ return size;
+}
+
+void MeshTexture::set_base_texture(const Ref<Texture> &p_texture) {
+ base_texture = p_texture;
+}
+
+Ref<Texture> MeshTexture::get_base_texture() const {
+ return base_texture;
+}
+
+void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const {
+
+ if (mesh.is_null() || base_texture.is_null()) {
+ return;
+ }
+ Transform2D xform;
+ xform.set_origin(p_pos);
+ if (p_transpose) {
+ SWAP(xform.elements[0][1], xform.elements[1][0]);
+ SWAP(xform.elements[0][0], xform.elements[1][1]);
+ }
+ RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
+ VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid);
+}
+void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const {
+ if (mesh.is_null() || base_texture.is_null()) {
+ return;
+ }
+ Transform2D xform;
+ Vector2 origin = p_rect.position;
+ if (p_rect.size.x < 0) {
+ origin.x += size.x;
+ }
+ if (p_rect.size.y < 0) {
+ origin.y += size.y;
+ }
+ xform.set_origin(origin);
+ xform.set_scale(p_rect.size / size);
+
+ if (p_transpose) {
+ SWAP(xform.elements[0][1], xform.elements[1][0]);
+ SWAP(xform.elements[0][0], xform.elements[1][1]);
+ }
+ RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
+ VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid);
+}
+void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map, bool p_clip_uv) const {
+
+ if (mesh.is_null() || base_texture.is_null()) {
+ return;
+ }
+ Transform2D xform;
+ Vector2 origin = p_rect.position;
+ if (p_rect.size.x < 0) {
+ origin.x += size.x;
+ }
+ if (p_rect.size.y < 0) {
+ origin.y += size.y;
+ }
+ xform.set_origin(origin);
+ xform.set_scale(p_rect.size / size);
+
+ if (p_transpose) {
+ SWAP(xform.elements[0][1], xform.elements[1][0]);
+ SWAP(xform.elements[0][0], xform.elements[1][1]);
+ }
+ RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
+ VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid);
+}
+bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
+ r_rect = p_rect;
+ r_src_rect = p_src_rect;
+ return true;
+}
+
+bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const {
+ return true;
+}
+
+void MeshTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshTexture::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &MeshTexture::get_mesh);
+ ClassDB::bind_method(D_METHOD("set_image_size", "size"), &MeshTexture::set_image_size);
+ ClassDB::bind_method(D_METHOD("get_image_size"), &MeshTexture::get_image_size);
+ ClassDB::bind_method(D_METHOD("set_base_texture", "texture"), &MeshTexture::set_base_texture);
+ ClassDB::bind_method(D_METHOD("get_base_texture"), &MeshTexture::get_base_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_base_texture", "get_base_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1"), "set_image_size", "get_image_size");
+}
+
+MeshTexture::MeshTexture() {
+}
+
//////////////////////////////////////////
int LargeTexture::get_width() const {
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 281a33929c..58287b7593 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -293,6 +293,49 @@ public:
AtlasTexture();
};
+class Mesh;
+
+class MeshTexture : public Texture {
+
+ GDCLASS(MeshTexture, Texture);
+ RES_BASE_EXTENSION("meshtex");
+
+ Ref<Texture> base_texture;
+ Ref<Mesh> mesh;
+ Size2i size;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_width() const;
+ virtual int get_height() const;
+ virtual RID get_rid() const;
+
+ virtual bool has_alpha() const;
+
+ virtual void set_flags(uint32_t p_flags);
+ virtual uint32_t get_flags() const;
+
+ void set_mesh(const Ref<Mesh> &p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ void set_image_size(const Size2 &p_size);
+ Size2 get_image_size() const;
+
+ void set_base_texture(const Ref<Texture> &p_texture);
+ Ref<Texture> get_base_texture() 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 Ref<Texture> &p_normal_map = Ref<Texture>()) const;
+ 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 Ref<Texture> &p_normal_map = Ref<Texture>()) const;
+ 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, const Ref<Texture> &p_normal_map = Ref<Texture>(), 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;
+
+ bool is_pixel_opaque(int p_x, int p_y) const;
+
+ MeshTexture();
+};
+
class LargeTexture : public Texture {
GDCLASS(LargeTexture, Texture);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 4ddfc0331b..d09fac47f0 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -215,7 +215,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = autotile_get_spacing(id);
else if (what == "bitmask_flags") {
Array p;
- for (Map<Vector2, uint16_t>::Element *E = tile_map[id].autotile_data.flags.front(); E; E = E->next()) {
+ 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());
}
@@ -319,6 +319,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
}
p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset"));
@@ -544,7 +545,7 @@ const Map<Vector2, int> &TileSet::autotile_get_z_index_map(int p_id) const {
return tile_map[p_id].autotile_data.z_index_map;
}
-void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint16_t p_flag) {
+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) {
@@ -555,7 +556,7 @@ void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint16_t p_flag) {
}
}
-uint16_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) {
+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)) {
@@ -564,13 +565,13 @@ uint16_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) {
return tile_map[p_id].autotile_data.flags[p_coord];
}
-const Map<Vector2, uint16_t> &TileSet::autotile_get_bitmask_map(int p_id) {
+const Map<Vector2, uint32_t> &TileSet::autotile_get_bitmask_map(int p_id) {
- static Map<Vector2, uint16_t> dummy;
- static Map<Vector2, uint16_t> dummy_atlas;
+ 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, uint16_t>();
+ 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);
@@ -600,22 +601,79 @@ Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask,
}
List<Vector2> coords;
- uint16_t mask;
- for (Map<Vector2, uint16_t>::Element *E = tile_map[p_id].autotile_data.flags.front(); E; E = E->next()) {
+ 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_BOTTOMLEFT | BIND_BOTTOMRIGHT | BIND_TOPLEFT | BIND_TOPRIGHT);
+ mask |= (BIND_IGNORE_TOP | BIND_IGNORE_LEFT | BIND_IGNORE_CENTER | BIND_IGNORE_RIGHT | BIND_IGNORE_BOTTOM);
+ }
+
+ mask_ = mask & 0xFFFF;
+ mask_ignore = mask >> 16;
+
+ 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());
}
- if (mask == p_bitmask) {
- for (int i = 0; i < autotile_get_subtile_priority(p_id, E->key()); i++) {
- coords.push_back(E->key());
+ }
+
+ if (coords.size() == 0) {
+ return autotile_get_icon_coordinate(p_id);
+ } 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;
+ }
+ lower_bound = upper_bound;
+ priorities_E = priorities_E->next();
+ coords_E = coords_E->next();
+ }
+
+ return result;
+ }
+}
+
+Vector2 TileSet::atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node, const Vector2 &p_tile_location) {
+
+ ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
+ //First try to forward selection to script
+ if (get_script_instance() != NULL) {
+ if (get_script_instance()->has_method("_forward_atlas_subtile_selection")) {
+ Variant ret = get_script_instance()->call("_forward_atlas_subtile_selection", p_id, p_tilemap_node, p_tile_location);
+ if (ret.get_type() == Variant::VECTOR2) {
+ return ret;
+ }
+ }
+ }
+
+ Vector2 coord = tile_get_region(p_id).size / autotile_get_size(p_id);
+
+ List<Vector2> coords;
+ for (int x = 0; x < coord.x; x++) {
+ for (int y = 0; y < coord.y; y++) {
+ for (int i = 0; i < autotile_get_subtile_priority(p_id, Vector2(x, y)); i++) {
+ coords.push_back(Vector2(x, y));
}
}
}
if (coords.size() == 0) {
return autotile_get_icon_coordinate(p_id);
} else {
- return coords[Math::rand() % coords.size()];
+ return coords[Math::random(0, (int)coords.size())];
}
}
@@ -1114,6 +1172,7 @@ void TileSet::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id")));
BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
+ BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_atlas_subtile_selection", PropertyInfo(Variant::INT, "atlastile_id"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
BIND_ENUM_CONSTANT(BITMASK_2X2);
BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index fac48243d0..5fc22b9fc6 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -72,7 +72,17 @@ public:
BIND_RIGHT = 32,
BIND_BOTTOMLEFT = 64,
BIND_BOTTOM = 128,
- BIND_BOTTOMRIGHT = 256
+ 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
};
enum TileMode {
@@ -86,7 +96,7 @@ public:
Size2 size;
int spacing;
Vector2 icon_coord;
- Map<Vector2, uint16_t> flags;
+ Map<Vector2, uint32_t> flags;
Map<Vector2, Ref<OccluderPolygon2D> > occluder_map;
Map<Vector2, Ref<NavigationPolygon> > navpoly_map;
Map<Vector2, int> priority_map;
@@ -181,10 +191,11 @@ public:
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;
- void autotile_set_bitmask(int p_id, Vector2 p_coord, uint16_t p_flag);
- uint16_t autotile_get_bitmask(int p_id, Vector2 p_coord);
- const Map<Vector2, uint16_t> &autotile_get_bitmask_map(int p_id);
+ 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 = NULL, const Vector2 &p_tile_location = Vector2());
+ Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = NULL, const Vector2 &p_tile_location = Vector2());
void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape);
Ref<Shape2D> tile_get_shape(int p_id, int p_shape_id) const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 4e5909eb2e..9b2e410985 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "visual_shader.h"
+
#include "core/vmap.h"
#include "servers/visual/shader_types.h"
@@ -159,6 +160,7 @@ Vector2 VisualShader::get_node_position(Type p_type, int p_id) const {
ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2());
return g->nodes[p_id].position;
}
+
Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const {
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>());
const Graph *g = &graph[p_type];
@@ -240,6 +242,9 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
if (!g->nodes.has(p_from_node))
return false;
+ if (p_from_node == p_to_node)
+ return false;
+
if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count())
return false;
@@ -252,7 +257,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port);
VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port);
- if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) {
+ if (MAX(0, from_port_type - 2) != (MAX(0, to_port_type - 2))) {
return false;
}
@@ -266,6 +271,18 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
return true;
}
+void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+ Connection c;
+ c.from_node = p_from_node;
+ c.from_port = p_from_port;
+ c.to_node = p_to_node;
+ c.to_port = p_to_port;
+ g->connections.push_back(c);
+ _queue_update();
+}
+
Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, ERR_CANT_CONNECT);
Graph *g = &graph[p_type];
@@ -278,8 +295,8 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port);
VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port);
- if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) {
- ERR_EXPLAIN("Incompatible port types (scalar/vec with transform");
+ if (MAX(0, from_port_type - 2) != (MAX(0, to_port_type - 2))) {
+ ERR_EXPLAIN("Incompatible port types (scalar/vec/bool with transform");
ERR_FAIL_V(ERR_INVALID_PARAMETER)
return ERR_INVALID_PARAMETER;
}
@@ -301,6 +318,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
_queue_update();
return OK;
}
+
void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
ERR_FAIL_INDEX(p_type, TYPE_MAX);
Graph *g = &graph[p_type];
@@ -331,6 +349,7 @@ Array VisualShader::_get_node_connections(Type p_type) const {
return ret;
}
+
void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connections) const {
ERR_FAIL_INDEX(p_type, TYPE_MAX);
const Graph *g = &graph[p_type];
@@ -456,6 +475,8 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) {
code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " );\n";
+ } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_BOOLEAN) {
+ code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0 );\n";
} else {
code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
}
@@ -472,6 +493,54 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_')
+String VisualShader::validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const {
+ String name = p_name;
+
+ while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ name = name.substr(1, name.length() - 1);
+ }
+
+ if (name != String()) {
+
+ String valid_name;
+
+ for (int i = 0; i < name.length(); i++) {
+ if (IS_SYMBOL_CHAR(name[i])) {
+ valid_name += String::chr(name[i]);
+ } else if (name[i] == ' ') {
+ valid_name += "_";
+ }
+ }
+
+ name = valid_name;
+ }
+
+ String valid_name = name;
+ bool is_equal = false;
+
+ for (int i = 0; i < p_input_ports.size(); i++) {
+ if (name == p_input_ports[i]) {
+ is_equal = true;
+ break;
+ }
+ }
+
+ if (!is_equal) {
+ for (int i = 0; i < p_output_ports.size(); i++) {
+ if (name == p_output_ports[i]) {
+ is_equal = true;
+ break;
+ }
+ }
+ }
+
+ if (is_equal) {
+ name = "";
+ }
+
+ return name;
+}
+
String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const {
String name = p_name; //validate name first
@@ -591,7 +660,7 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
Vector<int> conns = p_value;
if (conns.size() % 4 == 0) {
for (int i = 0; i < conns.size(); i += 4) {
- connect_nodes(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]);
+ connect_nodes_forced(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]);
}
}
return true;
@@ -606,6 +675,18 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "position") {
set_node_position(type, id, p_value);
return true;
+ } else if (what == "size") {
+ ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_size(p_value);
+ return true;
+ } else if (what == "input_ports") {
+ ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_inputs(p_value);
+ return true;
+ } else if (what == "output_ports") {
+ ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_outputs(p_value);
+ return true;
+ } else if (what == "expression") {
+ ((VisualShaderNodeExpression *)get_node(type, id).ptr())->set_expression(p_value);
+ return true;
}
}
return false;
@@ -663,6 +744,18 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "position") {
r_ret = get_node_position(type, id);
return true;
+ } else if (what == "size") {
+ r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_size();
+ return true;
+ } else if (what == "input_ports") {
+ r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_inputs();
+ return true;
+ } else if (what == "output_ports") {
+ r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_outputs();
+ return true;
+ } else if (what == "expression") {
+ r_ret = ((VisualShaderNodeExpression *)get_node(type, id).ptr())->get_expression();
+ return true;
}
}
return false;
@@ -722,6 +815,15 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
}
p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+
+ if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != NULL) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+ if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != NULL) {
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
}
p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
@@ -779,6 +881,14 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
inputs[i] = "dot(" + src_var + ",vec3(0.333333,0.333333,0.333333))";
} else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
inputs[i] = "vec3(" + src_var + ")";
+ } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
+ inputs[i] = "all(bvec3(" + src_var + "))";
+ } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
+ inputs[i] = src_var + ">0.0?true:false";
+ } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
+ inputs[i] = src_var + "?1.0:0.0";
+ } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
+ inputs[i] = "vec3(" + src_var + "?1.0:0.0)";
}
} else {
@@ -787,6 +897,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
float val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n";
+ } else if (defval.get_type() == Variant::BOOL) {
+ bool val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ code += "\nbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
} else if (defval.get_type() == Variant::VECTOR3) {
Vector3 val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
@@ -823,8 +937,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_SCALAR: code += String() + "\tfloat " + outputs[i] + ";\n"; break;
case VisualShaderNode::PORT_TYPE_VECTOR: code += String() + "\tvec3 " + outputs[i] + ";\n"; break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: code += String() + "\tbool " + outputs[i] + ";\n"; break;
case VisualShaderNode::PORT_TYPE_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break;
- default: {}
+ default: {
+ }
}
}
@@ -974,26 +1090,33 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) {
}
}
+void VisualShader::rebuild() {
+ dirty = true;
+ _update_shader();
+}
+
void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShader::set_mode);
ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node);
- ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position);
-
ClassDB::bind_method(D_METHOD("get_node", "type", "id"), &VisualShader::get_node);
+
+ ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position);
ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position);
ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list);
ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id);
ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node);
+ ClassDB::bind_method(D_METHOD("rebuild"), &VisualShader::rebuild);
ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
ClassDB::bind_method(D_METHOD("connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes);
ClassDB::bind_method(D_METHOD("disconnect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::disconnect_nodes);
+ ClassDB::bind_method(D_METHOD("connect_nodes_forced", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes_forced);
ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections);
@@ -1262,6 +1385,11 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_TRANSFORM: {
code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n";
} break; //default (none found) is scalar
+ case PORT_TYPE_BOOLEAN: {
+ code = "\t" + p_output_vars[0] + " = false;\n";
+ } break;
+ default:
+ break;
}
}
@@ -1603,3 +1731,555 @@ void VisualShaderNodeUniform::_bind_methods() {
VisualShaderNodeUniform::VisualShaderNodeUniform() {
}
+
+////////////// GroupBase
+
+String VisualShaderNodeGroupBase::get_caption() const {
+ return "Group";
+}
+
+void VisualShaderNodeGroupBase::set_size(const Vector2 &p_size) {
+ size = p_size;
+}
+
+Vector2 VisualShaderNodeGroupBase::get_size() const {
+ return size;
+}
+
+void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) {
+
+ if (inputs == p_inputs)
+ return;
+
+ clear_input_ports();
+
+ inputs = p_inputs;
+
+ Vector<String> input_strings = inputs.split(";", false);
+
+ int input_port_count = input_strings.size();
+
+ for (int i = 0; i < input_port_count; i++) {
+
+ Vector<String> arr = input_strings[i].split(",");
+
+ int port_idx = arr[0].to_int();
+ int port_type = arr[1].to_int();
+ String port_name = arr[2];
+
+ Port port;
+ port.type = (PortType)port_type;
+ port.name = port_name;
+ input_ports[port_idx] = port;
+ }
+}
+
+String VisualShaderNodeGroupBase::get_inputs() const {
+ return inputs;
+}
+
+void VisualShaderNodeGroupBase::set_outputs(const String &p_outputs) {
+
+ if (outputs == p_outputs)
+ return;
+
+ clear_output_ports();
+
+ outputs = p_outputs;
+
+ Vector<String> output_strings = outputs.split(";", false);
+
+ int output_port_count = output_strings.size();
+
+ for (int i = 0; i < output_port_count; i++) {
+
+ Vector<String> arr = output_strings[i].split(",");
+
+ int port_idx = arr[0].to_int();
+ int port_type = arr[1].to_int();
+ String port_name = arr[2];
+
+ Port port;
+ port.type = (PortType)port_type;
+ port.name = port_name;
+ output_ports[port_idx] = port;
+ }
+}
+
+String VisualShaderNodeGroupBase::get_outputs() const {
+ return outputs;
+}
+
+void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
+
+ String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int index = 0;
+ if (p_id < inputs_strings.size()) {
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ if (i == p_id) {
+ inputs = inputs.insert(index, str);
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+ } else {
+ inputs += str;
+ }
+
+ inputs_strings = inputs.split(";", false);
+ index = 0;
+
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ int count = 0;
+ for (int j = 0; j < inputs_strings[i].size(); j++) {
+ if (inputs_strings[i][j] == ',') {
+ break;
+ }
+ count++;
+ }
+
+ inputs.erase(index, count);
+ inputs = inputs.insert(index, itos(i));
+ index += inputs_strings[i].size();
+ }
+
+ _apply_port_changes();
+}
+
+void VisualShaderNodeGroupBase::remove_input_port(int p_id) {
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ count = inputs_strings[i].size();
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+ inputs.erase(index, count);
+
+ inputs_strings = inputs.split(";", false);
+ for (int i = p_id; i < inputs_strings.size(); i++) {
+ inputs = inputs.replace_first(inputs_strings[i].split(",")[0], itos(i));
+ }
+
+ _apply_port_changes();
+}
+
+int VisualShaderNodeGroupBase::get_input_port_count() const {
+ return input_ports.size();
+}
+
+bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
+ return input_ports.has(p_id);
+}
+
+void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
+
+ String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
+ Vector<String> outputs_strings = outputs.split(";", false);
+ int index = 0;
+ if (p_id < outputs_strings.size()) {
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ if (i == p_id) {
+ outputs = outputs.insert(index, str);
+ break;
+ }
+ index += outputs_strings[i].size();
+ }
+ } else {
+ outputs += str;
+ }
+
+ outputs_strings = outputs.split(";", false);
+ index = 0;
+
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ int count = 0;
+ for (int j = 0; j < outputs_strings[i].size(); j++) {
+ if (outputs_strings[i][j] == ',') {
+ break;
+ }
+ count++;
+ }
+
+ outputs.erase(index, count);
+ outputs = outputs.insert(index, itos(i));
+ index += outputs_strings[i].size();
+ }
+
+ _apply_port_changes();
+}
+
+void VisualShaderNodeGroupBase::remove_output_port(int p_id) {
+
+ Vector<String> outputs_strings = outputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ Vector<String> arr = outputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ count = outputs_strings[i].size();
+ break;
+ }
+ index += outputs_strings[i].size();
+ }
+ outputs.erase(index, count);
+
+ outputs_strings = outputs.split(";", false);
+ for (int i = p_id; i < outputs_strings.size(); i++) {
+ outputs = outputs.replace_first(outputs_strings[i].split(",")[0], itos(i));
+ }
+
+ _apply_port_changes();
+}
+
+int VisualShaderNodeGroupBase::get_output_port_count() const {
+ return output_ports.size();
+}
+
+bool VisualShaderNodeGroupBase::has_output_port(int p_id) const {
+ return output_ports.has(p_id);
+}
+
+void VisualShaderNodeGroupBase::clear_input_ports() {
+ input_ports.clear();
+}
+
+void VisualShaderNodeGroupBase::clear_output_ports() {
+ output_ports.clear();
+}
+
+void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
+
+ if (input_ports[p_id].type == p_type)
+ return;
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size();
+ count = arr[1].size() - 1;
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+
+ inputs.erase(index, count);
+
+ inputs = inputs.insert(index, itos(p_type));
+
+ _apply_port_changes();
+}
+
+VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_input_port_type(int p_id) const {
+ ERR_FAIL_COND_V(!input_ports.has(p_id), (PortType)0);
+ return input_ports[p_id].type;
+}
+
+void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_name) {
+
+ if (input_ports[p_id].name == p_name)
+ return;
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size() + arr[1].size();
+ count = arr[2].size() - 1;
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+
+ inputs.erase(index, count);
+
+ inputs = inputs.insert(index, p_name);
+
+ _apply_port_changes();
+}
+
+String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const {
+ ERR_FAIL_COND_V(!input_ports.has(p_id), "");
+ return input_ports[p_id].name;
+}
+
+void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
+
+ if (output_ports[p_id].type == p_type)
+ return;
+
+ Vector<String> output_strings = outputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < output_strings.size(); i++) {
+ Vector<String> arr = output_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size();
+ count = arr[1].size() - 1;
+ break;
+ }
+ index += output_strings[i].size();
+ }
+
+ outputs.erase(index, count);
+
+ outputs = outputs.insert(index, itos(p_type));
+
+ _apply_port_changes();
+}
+
+VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_output_port_type(int p_id) const {
+ ERR_FAIL_COND_V(!output_ports.has(p_id), (PortType)0);
+ return output_ports[p_id].type;
+}
+
+void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_name) {
+ if (output_ports[p_id].name == p_name)
+ return;
+
+ Vector<String> output_strings = outputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < output_strings.size(); i++) {
+ Vector<String> arr = output_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size() + arr[1].size();
+ count = arr[2].size() - 1;
+ break;
+ }
+ index += output_strings[i].size();
+ }
+
+ outputs.erase(index, count);
+
+ outputs = outputs.insert(index, p_name);
+
+ _apply_port_changes();
+}
+
+String VisualShaderNodeGroupBase::get_output_port_name(int p_id) const {
+ ERR_FAIL_COND_V(!output_ports.has(p_id), "");
+ return output_ports[p_id].name;
+}
+
+int VisualShaderNodeGroupBase::get_free_input_port_id() const {
+ return input_ports.size();
+}
+
+int VisualShaderNodeGroupBase::get_free_output_port_id() const {
+ return output_ports.size();
+}
+
+void VisualShaderNodeGroupBase::set_control(Control *p_control, int p_index) {
+ controls[p_index] = p_control;
+}
+
+Control *VisualShaderNodeGroupBase::get_control(int p_index) {
+ ERR_FAIL_COND_V(!controls.has(p_index), NULL);
+ return controls[p_index];
+}
+
+void VisualShaderNodeGroupBase::_apply_port_changes() {
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ Vector<String> outputs_strings = outputs.split(";", false);
+
+ clear_input_ports();
+ clear_output_ports();
+
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ Port port;
+ port.type = (PortType)arr[1].to_int();
+ port.name = arr[2];
+ input_ports[i] = port;
+ }
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ Vector<String> arr = outputs_strings[i].split(",");
+ Port port;
+ port.type = (PortType)arr[1].to_int();
+ port.name = arr[2];
+ output_ports[i] = port;
+ }
+}
+
+void VisualShaderNodeGroupBase::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeGroupBase::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeGroupBase::get_size);
+
+ ClassDB::bind_method(D_METHOD("set_inputs", "inputs"), &VisualShaderNodeGroupBase::set_inputs);
+ ClassDB::bind_method(D_METHOD("get_inputs"), &VisualShaderNodeGroupBase::get_inputs);
+
+ ClassDB::bind_method(D_METHOD("set_outputs", "outputs"), &VisualShaderNodeGroupBase::set_outputs);
+ ClassDB::bind_method(D_METHOD("get_outputs"), &VisualShaderNodeGroupBase::get_outputs);
+
+ ClassDB::bind_method(D_METHOD("add_input_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_input_port);
+ ClassDB::bind_method(D_METHOD("remove_input_port", "id"), &VisualShaderNodeGroupBase::remove_input_port);
+ ClassDB::bind_method(D_METHOD("get_input_port_count"), &VisualShaderNodeGroupBase::get_input_port_count);
+ ClassDB::bind_method(D_METHOD("has_input_port", "id"), &VisualShaderNodeGroupBase::has_input_port);
+ ClassDB::bind_method(D_METHOD("clear_input_ports"), &VisualShaderNodeGroupBase::clear_input_ports);
+
+ ClassDB::bind_method(D_METHOD("add_output_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_output_port);
+ ClassDB::bind_method(D_METHOD("remove_output_port", "id"), &VisualShaderNodeGroupBase::remove_output_port);
+ ClassDB::bind_method(D_METHOD("get_output_port_count"), &VisualShaderNodeGroupBase::get_output_port_count);
+ ClassDB::bind_method(D_METHOD("has_output_port", "id"), &VisualShaderNodeGroupBase::has_output_port);
+ ClassDB::bind_method(D_METHOD("clear_output_ports"), &VisualShaderNodeGroupBase::clear_output_ports);
+
+ ClassDB::bind_method(D_METHOD("set_input_port_name"), &VisualShaderNodeGroupBase::set_input_port_name);
+ ClassDB::bind_method(D_METHOD("set_input_port_type"), &VisualShaderNodeGroupBase::set_input_port_type);
+ ClassDB::bind_method(D_METHOD("set_output_port_name"), &VisualShaderNodeGroupBase::set_output_port_name);
+ ClassDB::bind_method(D_METHOD("set_output_port_type"), &VisualShaderNodeGroupBase::set_output_port_type);
+
+ ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id);
+ ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id);
+
+ ClassDB::bind_method(D_METHOD("set_control", "control", "index"), &VisualShaderNodeGroupBase::set_control);
+ ClassDB::bind_method(D_METHOD("get_control", "index"), &VisualShaderNodeGroupBase::get_control);
+}
+
+String VisualShaderNodeGroupBase::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "";
+}
+
+VisualShaderNodeGroupBase::VisualShaderNodeGroupBase() {
+ size = Size2(0, 0);
+ inputs = "";
+ outputs = "";
+}
+
+////////////// Expression
+
+String VisualShaderNodeExpression::get_caption() const {
+ return "Expression";
+}
+
+void VisualShaderNodeExpression::set_expression(const String &p_expression) {
+ expression = p_expression;
+}
+
+void VisualShaderNodeExpression::build() {
+ emit_changed();
+}
+
+String VisualShaderNodeExpression::get_expression() const {
+ return expression;
+}
+
+String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String _expression = expression;
+
+ _expression = _expression.insert(0, "\n");
+ _expression = _expression.replace("\n", "\n\t\t");
+
+ static Vector<String> pre_symbols;
+ if (pre_symbols.empty()) {
+ pre_symbols.push_back("\t");
+ pre_symbols.push_back("{");
+ pre_symbols.push_back("[");
+ pre_symbols.push_back("(");
+ pre_symbols.push_back(" ");
+ pre_symbols.push_back("-");
+ pre_symbols.push_back("*");
+ pre_symbols.push_back("/");
+ pre_symbols.push_back("+");
+ pre_symbols.push_back("=");
+ pre_symbols.push_back("&");
+ pre_symbols.push_back("|");
+ pre_symbols.push_back("!");
+ }
+
+ static Vector<String> post_symbols;
+ if (post_symbols.empty()) {
+ post_symbols.push_back("\0");
+ post_symbols.push_back("\t");
+ post_symbols.push_back("\n");
+ post_symbols.push_back(";");
+ post_symbols.push_back("}");
+ post_symbols.push_back("]");
+ post_symbols.push_back(")");
+ post_symbols.push_back(" ");
+ post_symbols.push_back(".");
+ post_symbols.push_back("-");
+ post_symbols.push_back("*");
+ post_symbols.push_back("/");
+ post_symbols.push_back("+");
+ post_symbols.push_back("=");
+ post_symbols.push_back("&");
+ post_symbols.push_back("|");
+ post_symbols.push_back("!");
+ }
+
+ for (int i = 0; i < get_input_port_count(); i++) {
+ for (int j = 0; j < pre_symbols.size(); j++) {
+ for (int k = 0; k < post_symbols.size(); k++) {
+ _expression = _expression.replace(pre_symbols[j] + get_input_port_name(i) + post_symbols[k], pre_symbols[j] + p_input_vars[i] + post_symbols[k]);
+ }
+ }
+ }
+ for (int i = 0; i < get_output_port_count(); i++) {
+ for (int j = 0; j < pre_symbols.size(); j++) {
+ for (int k = 0; k < post_symbols.size(); k++) {
+ _expression = _expression.replace(pre_symbols[j] + get_output_port_name(i) + post_symbols[k], pre_symbols[j] + p_output_vars[i] + post_symbols[k]);
+ }
+ }
+ }
+
+ String output_initializer;
+
+ for (int i = 0; i < get_output_port_count(); i++) {
+ int port_type = get_output_port_type(i);
+ String tk = "";
+ switch (port_type) {
+ case PORT_TYPE_SCALAR:
+ tk = "0.0";
+ break;
+ case PORT_TYPE_VECTOR:
+ tk = "vec3(0.0, 0.0, 0.0)";
+ break;
+ case PORT_TYPE_BOOLEAN:
+ tk = "false";
+ break;
+ case PORT_TYPE_TRANSFORM:
+ tk = "mat4(1.0)";
+ break;
+ default:
+ continue;
+ }
+ output_initializer += "\t" + p_output_vars[i] + "=" + tk + ";\n";
+ }
+
+ String code;
+ code += output_initializer;
+ code += "\t{";
+ code += _expression;
+ code += "\n\t}";
+
+ return code;
+}
+
+void VisualShaderNodeExpression::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_expression", "expression"), &VisualShaderNodeExpression::set_expression);
+ ClassDB::bind_method(D_METHOD("get_expression"), &VisualShaderNodeExpression::get_expression);
+
+ ClassDB::bind_method(D_METHOD("build"), &VisualShaderNodeExpression::build);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "expression"), "set_expression", "get_expression");
+}
+
+VisualShaderNodeExpression::VisualShaderNodeExpression() {
+ expression = "";
+}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 4792038351..838c2c618d 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -32,6 +32,7 @@
#define VISUAL_SHADER_H
#include "core/string_builder.h"
+#include "scene/gui/control.h"
#include "scene/resources/shader.h"
class VisualShaderNodeUniform;
@@ -135,7 +136,9 @@ public:
bool can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
Error connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ void connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ void rebuild();
void get_node_connections(Type p_type, List<Connection> *r_connections) const;
void set_mode(Mode p_mode);
@@ -148,6 +151,7 @@ public:
String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
+ String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const;
String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const;
VisualShader();
@@ -175,7 +179,9 @@ public:
enum PortType {
PORT_TYPE_SCALAR,
PORT_TYPE_VECTOR,
+ PORT_TYPE_BOOLEAN,
PORT_TYPE_TRANSFORM,
+ PORT_TYPE_COLOR // just a hint for node tree icons, do not use it as actual port type !
};
virtual String get_caption() const = 0;
@@ -312,4 +318,93 @@ public:
VisualShaderNodeUniform();
};
+class VisualShaderNodeGroupBase : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode)
+private:
+ void _apply_port_changes();
+
+protected:
+ Vector2 size;
+ String inputs;
+ String outputs;
+
+ struct Port {
+ PortType type;
+ String name;
+ };
+
+ Map<int, Port> input_ports;
+ Map<int, Port> output_ports;
+ Map<int, Control *> controls;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_size(const Vector2 &p_size);
+ Vector2 get_size() const;
+
+ void set_inputs(const String &p_inputs);
+ String get_inputs() const;
+
+ void set_outputs(const String &p_outputs);
+ String get_outputs() const;
+
+ void add_input_port(int p_id, int p_type, const String &p_name);
+ void remove_input_port(int p_id);
+ virtual int get_input_port_count() const;
+ bool has_input_port(int p_id) const;
+ void clear_input_ports();
+
+ void add_output_port(int p_id, int p_type, const String &p_name);
+ void remove_output_port(int p_id);
+ virtual int get_output_port_count() const;
+ bool has_output_port(int p_id) const;
+ void clear_output_ports();
+
+ void set_input_port_type(int p_id, int p_type);
+ virtual PortType get_input_port_type(int p_id) const;
+ void set_input_port_name(int p_id, const String &p_name);
+ virtual String get_input_port_name(int p_id) const;
+
+ void set_output_port_type(int p_id, int p_type);
+ virtual PortType get_output_port_type(int p_id) const;
+ void set_output_port_name(int p_id, const String &p_name);
+ virtual String get_output_port_name(int p_id) const;
+
+ int get_free_input_port_id() const;
+ int get_free_output_port_id() const;
+
+ void set_control(Control *p_control, int p_index);
+ Control *get_control(int p_index);
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeGroupBase();
+};
+
+class VisualShaderNodeExpression : public VisualShaderNodeGroupBase {
+ GDCLASS(VisualShaderNodeExpression, VisualShaderNodeGroupBase)
+
+private:
+ String expression;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_expression(const String &p_expression);
+ String get_expression() const;
+
+ void build();
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeExpression();
+};
+
#endif // VISUAL_SHADER_H
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index e47069d723..a44471a365 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -88,6 +88,67 @@ VisualShaderNodeScalarConstant::VisualShaderNodeScalarConstant() {
constant = 0;
}
+////////////// Boolean
+
+String VisualShaderNodeBooleanConstant::get_caption() const {
+ return "Boolean";
+}
+
+int VisualShaderNodeBooleanConstant::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeBooleanConstant::PortType VisualShaderNodeBooleanConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_BOOLEAN;
+}
+
+String VisualShaderNodeBooleanConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeBooleanConstant::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeBooleanConstant::PortType VisualShaderNodeBooleanConstant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_BOOLEAN;
+}
+
+String VisualShaderNodeBooleanConstant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeBooleanConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n";
+}
+
+void VisualShaderNodeBooleanConstant::set_constant(bool p_value) {
+ constant = p_value;
+ emit_changed();
+}
+
+bool VisualShaderNodeBooleanConstant::get_constant() const {
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeBooleanConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeBooleanConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeBooleanConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeBooleanConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeBooleanConstant::VisualShaderNodeBooleanConstant() {
+ constant = false;
+}
+
////////////// Color
String VisualShaderNodeColorConstant::get_caption() const {
@@ -666,6 +727,7 @@ String VisualShaderNodeScalarOp::generate_code(Shader::Mode p_mode, VisualShader
case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_STEP: code += "step( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
}
return code;
@@ -693,7 +755,7 @@ void VisualShaderNodeScalarOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeScalarOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeScalarOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2,Step"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -704,6 +766,7 @@ void VisualShaderNodeScalarOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MAX);
BIND_ENUM_CONSTANT(OP_MIN);
BIND_ENUM_CONSTANT(OP_ATAN2);
+ BIND_ENUM_CONSTANT(OP_STEP);
}
VisualShaderNodeScalarOp::VisualShaderNodeScalarOp() {
@@ -752,6 +815,9 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
case OP_CROSS: code += "cross( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_REFLECT: code += "reflect( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_STEP: code += "step( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
}
return code;
@@ -779,7 +845,7 @@ void VisualShaderNodeVectorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross,Atan2,Reflect,Step"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -790,6 +856,9 @@ void VisualShaderNodeVectorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MAX);
BIND_ENUM_CONSTANT(OP_MIN);
BIND_ENUM_CONSTANT(OP_CROSS);
+ BIND_ENUM_CONSTANT(OP_ATAN2);
+ BIND_ENUM_CONSTANT(OP_REFLECT);
+ BIND_ENUM_CONSTANT(OP_STEP);
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
@@ -976,8 +1045,12 @@ String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualS
if (op == OP_AxB) {
return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
- } else {
+ } else if (op == OP_BxA) {
return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
+ } else if (op == OP_AxB_COMP) {
+ return "\t" + p_output_vars[0] + " = matrixCompMult( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n";
+ } else {
+ return "\t" + p_output_vars[0] + " = matrixCompMult( " + p_input_vars[1] + " , " + p_input_vars[0] + " );\n";
}
}
@@ -1003,10 +1076,12 @@ void VisualShaderNodeTransformMult::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B(per component),B x A(per component)"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_AxB);
BIND_ENUM_CONSTANT(OP_BxA);
+ BIND_ENUM_CONSTANT(OP_AxB_COMP);
+ BIND_ENUM_CONSTANT(OP_BxA_COMP);
}
VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() {
@@ -1117,7 +1192,7 @@ String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const {
String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *scalar_func_id[FUNC_NEGATE + 1] = {
+ static const char *scalar_func_id[FUNC_ONEMINUS + 1] = {
"sin($)",
"cos($)",
"tan($)",
@@ -1138,6 +1213,18 @@ String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShad
"fract($)",
"min(max($,0.0),1.0)",
"-($)",
+ "acosh($)",
+ "asinh($)",
+ "atanh($)",
+ "degrees($)",
+ "exp2($)",
+ "inversesqrt($)",
+ "log2($)",
+ "radians($)",
+ "1.0/($)",
+ "roundEven($)",
+ "trunc($)",
+ "1.0-$"
};
return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
@@ -1165,7 +1252,7 @@ void VisualShaderNodeScalarFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc,OneMinus"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_SIN);
BIND_ENUM_CONSTANT(FUNC_COS);
@@ -1187,6 +1274,18 @@ void VisualShaderNodeScalarFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_FRAC);
BIND_ENUM_CONSTANT(FUNC_SATURATE);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
+ BIND_ENUM_CONSTANT(FUNC_ACOSH);
+ BIND_ENUM_CONSTANT(FUNC_ASINH);
+ BIND_ENUM_CONSTANT(FUNC_ATANH);
+ BIND_ENUM_CONSTANT(FUNC_DEGREES);
+ BIND_ENUM_CONSTANT(FUNC_EXP2);
+ BIND_ENUM_CONSTANT(FUNC_INVERSE_SQRT);
+ BIND_ENUM_CONSTANT(FUNC_LOG2);
+ BIND_ENUM_CONSTANT(FUNC_RADIANS);
+ BIND_ENUM_CONSTANT(FUNC_RECIPROCAL);
+ BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN);
+ BIND_ENUM_CONSTANT(FUNC_TRUNC);
+ BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
}
VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() {
@@ -1222,13 +1321,42 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *vec_func_id[FUNC_HSV2RGB + 1] = {
+ static const char *vec_func_id[FUNC_ONEMINUS + 1] = {
"normalize($)",
"max(min($,vec3(1.0)),vec3(0.0))",
"-($)",
"1.0/($)",
"",
"",
+ "abs($)",
+ "acos($)",
+ "acosh($)",
+ "asin($)",
+ "asinh($)",
+ "atan($)",
+ "atanh($)",
+ "ceil($)",
+ "cos($)",
+ "cosh($)",
+ "degrees($)",
+ "exp($)",
+ "exp2($)",
+ "floor($)",
+ "fract($)",
+ "inversesqrt($)",
+ "log($)",
+ "log2($)",
+ "radians($)",
+ "round($)",
+ "roundEven($)",
+ "sign($)",
+ "sin($)",
+ "sinh($)",
+ "sqrt($)",
+ "tan($)",
+ "tanh($)",
+ "trunc($)",
+ "vec3(1.0, 1.0, 1.0)-$"
};
String code;
@@ -1280,7 +1408,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_NORMALIZE);
BIND_ENUM_CONSTANT(FUNC_SATURATE);
@@ -1288,6 +1416,35 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_RECIPROCAL);
BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
+ BIND_ENUM_CONSTANT(FUNC_ABS);
+ BIND_ENUM_CONSTANT(FUNC_ACOS);
+ BIND_ENUM_CONSTANT(FUNC_ACOSH);
+ BIND_ENUM_CONSTANT(FUNC_ASIN);
+ BIND_ENUM_CONSTANT(FUNC_ASINH);
+ BIND_ENUM_CONSTANT(FUNC_ATAN);
+ BIND_ENUM_CONSTANT(FUNC_ATANH);
+ BIND_ENUM_CONSTANT(FUNC_CEIL);
+ BIND_ENUM_CONSTANT(FUNC_COS);
+ BIND_ENUM_CONSTANT(FUNC_COSH);
+ BIND_ENUM_CONSTANT(FUNC_DEGREES);
+ BIND_ENUM_CONSTANT(FUNC_EXP);
+ BIND_ENUM_CONSTANT(FUNC_EXP2);
+ BIND_ENUM_CONSTANT(FUNC_FLOOR);
+ BIND_ENUM_CONSTANT(FUNC_FRAC);
+ BIND_ENUM_CONSTANT(FUNC_INVERSE_SQRT);
+ BIND_ENUM_CONSTANT(FUNC_LOG);
+ BIND_ENUM_CONSTANT(FUNC_LOG2);
+ BIND_ENUM_CONSTANT(FUNC_RADIANS);
+ BIND_ENUM_CONSTANT(FUNC_ROUND);
+ BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN);
+ BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_SIN);
+ BIND_ENUM_CONSTANT(FUNC_SINH);
+ BIND_ENUM_CONSTANT(FUNC_SQRT);
+ BIND_ENUM_CONSTANT(FUNC_TAN);
+ BIND_ENUM_CONSTANT(FUNC_TANH);
+ BIND_ENUM_CONSTANT(FUNC_TRUNC);
+ BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
@@ -1295,6 +1452,172 @@ VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
set_input_port_default_value(0, Vector3());
}
+////////////// ColorFunc
+
+String VisualShaderNodeColorFunc::get_caption() const {
+ return "ColorFunc";
+}
+
+int VisualShaderNodeColorFunc::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeColorFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeColorFunc::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeColorFunc::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String code;
+
+ switch (func) {
+ case FUNC_GRAYSCALE:
+ code += "\t{\n";
+ code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
+ code += "\t\tfloat max1 = max(c.r, c.g);\n";
+ code += "\t\tfloat max2 = max(max1, c.b);\n";
+ code += "\t\tfloat max3 = max(max1, max2);\n";
+ code += "\t\t" + p_output_vars[0] + " = vec3(max3, max3, max3);\n";
+ code += "\t}\n";
+ break;
+ case FUNC_SEPIA:
+ code += "\t{\n";
+ code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
+ code += "\t\tfloat r = (c.r * .393) + (c.g *.769) + (c.b * .189);\n";
+ code += "\t\tfloat g = (c.r * .349) + (c.g *.686) + (c.b * .168);\n";
+ code += "\t\tfloat b = (c.r * .272) + (c.g *.534) + (c.b * .131);\n";
+ code += "\t\t" + p_output_vars[0] + " = vec3(r, g, b);\n";
+ code += "\t}\n";
+ break;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeColorFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeColorFunc::Function VisualShaderNodeColorFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeColorFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeColorFunc::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
+ BIND_ENUM_CONSTANT(FUNC_SEPIA);
+}
+
+VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
+ func = FUNC_GRAYSCALE;
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Transform Func
+
+String VisualShaderNodeTransformFunc::get_caption() const {
+ return "TransformFunc";
+}
+
+int VisualShaderNodeTransformFunc::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTransformFunc::PortType VisualShaderNodeTransformFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+
+String VisualShaderNodeTransformFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeTransformFunc::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTransformFunc::PortType VisualShaderNodeTransformFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+
+String VisualShaderNodeTransformFunc::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeTransformFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ static const char *funcs[FUNC_TRANSPOSE + 1] = {
+ "inverse($)",
+ "transpose($)"
+ };
+
+ String code;
+ code += "\t" + p_output_vars[0] + "=" + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ return code;
+}
+
+void VisualShaderNodeTransformFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeTransformFunc::Function VisualShaderNodeTransformFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeTransformFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeTransformFunc::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeTransformFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeTransformFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Inverse,Transpose"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_INVERSE);
+ BIND_ENUM_CONSTANT(FUNC_TRANSPOSE);
+}
+
+VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
+ func = FUNC_INVERSE;
+ set_input_port_default_value(0, Transform());
+}
+
////////////// Dot Product
String VisualShaderNodeDotProduct::get_caption() const {
@@ -1364,10 +1687,674 @@ VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() {
set_input_port_default_value(0, Vector3());
}
+////////////// Determinant
+
+String VisualShaderNodeDeterminant::get_caption() const {
+ return "Determinant";
+}
+
+int VisualShaderNodeDeterminant::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScalarClamp::PortType VisualShaderNodeDeterminant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+
+String VisualShaderNodeDeterminant::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeDeterminant::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeDeterminant::PortType VisualShaderNodeDeterminant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeDeterminant::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeDeterminant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = determinant( " + p_input_vars[0] + " );\n";
+}
+
+VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() {
+ set_input_port_default_value(0, Transform());
+}
+
+////////////// Scalar Derivative Function
+
+String VisualShaderNodeScalarDerivativeFunc::get_caption() const {
+ return "ScalarDerivativeFunc";
+}
+
+int VisualShaderNodeScalarDerivativeFunc::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeScalarDerivativeFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeScalarDerivativeFunc::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ static const char *funcs[FUNC_Y + 1] = {
+ "fwidth($)",
+ "dFdx($)",
+ "dFdy($)"
+ };
+
+ String code;
+ code += "\t" + p_output_vars[0] + "=" + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ return code;
+}
+
+void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeScalarDerivativeFunc::Function VisualShaderNodeScalarDerivativeFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeScalarDerivativeFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarDerivativeFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarDerivativeFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_SUM);
+ BIND_ENUM_CONSTANT(FUNC_X);
+ BIND_ENUM_CONSTANT(FUNC_Y);
+}
+
+VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
+ func = FUNC_SUM;
+ set_input_port_default_value(0, 0.0);
+}
+
+////////////// Vector Derivative Function
+
+String VisualShaderNodeVectorDerivativeFunc::get_caption() const {
+ return "VectorDerivativeFunc";
+}
+
+int VisualShaderNodeVectorDerivativeFunc::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorDerivativeFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeVectorDerivativeFunc::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ static const char *funcs[FUNC_Y + 1] = {
+ "fwidth($)",
+ "dFdx($)",
+ "dFdy($)"
+ };
+
+ String code;
+ code += "\t" + p_output_vars[0] + "=" + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ return code;
+}
+
+void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeVectorDerivativeFunc::Function VisualShaderNodeVectorDerivativeFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeVectorDerivativeFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorDerivativeFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorDerivativeFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_SUM);
+ BIND_ENUM_CONSTANT(FUNC_X);
+ BIND_ENUM_CONSTANT(FUNC_Y);
+}
+
+VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
+ func = FUNC_SUM;
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Scalar Clamp
+
+String VisualShaderNodeScalarClamp::get_caption() const {
+ return "ScalarClamp";
+}
+
+int VisualShaderNodeScalarClamp::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeScalarClamp::PortType VisualShaderNodeScalarClamp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeScalarClamp::get_input_port_name(int p_port) const {
+ if (p_port == 0)
+ return "";
+ else if (p_port == 1)
+ return "min";
+ else if (p_port == 2)
+ return "max";
+ return "";
+}
+
+int VisualShaderNodeScalarClamp::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScalarClamp::PortType VisualShaderNodeScalarClamp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeScalarClamp::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeScalarClamp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = clamp( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeScalarClamp::VisualShaderNodeScalarClamp() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 1.0);
+}
+
+////////////// Vector Clamp
+
+String VisualShaderNodeVectorClamp::get_caption() const {
+ return "VectorClamp";
+}
+
+int VisualShaderNodeVectorClamp::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorClamp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorClamp::get_input_port_name(int p_port) const {
+ if (p_port == 0)
+ return "";
+ else if (p_port == 1)
+ return "min";
+ else if (p_port == 2)
+ return "max";
+ return "";
+}
+
+int VisualShaderNodeVectorClamp::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorClamp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorClamp::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorClamp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = clamp( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorClamp::VisualShaderNodeVectorClamp() {
+ set_input_port_default_value(0, Vector3(0, 0, 0));
+ set_input_port_default_value(1, Vector3(0, 0, 0));
+ set_input_port_default_value(2, Vector3(1, 1, 1));
+}
+
+////////////// FaceForward
+
+String VisualShaderNodeFaceForward::get_caption() const {
+ return "FaceForward";
+}
+
+int VisualShaderNodeFaceForward::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeFaceForward::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "N";
+ case 1:
+ return "I";
+ case 2:
+ return "Nref";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeFaceForward::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeFaceForward::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeFaceForward::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = faceforward( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeFaceForward::VisualShaderNodeFaceForward() {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Outer Product
+
+String VisualShaderNodeOuterProduct::get_caption() const {
+ return "OuterProduct";
+}
+
+int VisualShaderNodeOuterProduct::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeFaceForward::PortType VisualShaderNodeOuterProduct::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeOuterProduct::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "c";
+ case 1:
+ return "r";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeOuterProduct::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeOuterProduct::PortType VisualShaderNodeOuterProduct::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+
+String VisualShaderNodeOuterProduct::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeOuterProduct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = outerProduct( " + p_input_vars[0] + ", " + p_input_vars[1] + " );\n";
+}
+
+VisualShaderNodeOuterProduct::VisualShaderNodeOuterProduct() {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Vector-Scalar Step
+
+String VisualShaderNodeVectorScalarStep::get_caption() const {
+ return "VectorScalarStep";
+}
+
+int VisualShaderNodeVectorScalarStep::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeVectorScalarStep::PortType VisualShaderNodeVectorScalarStep::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorScalarStep::get_input_port_name(int p_port) const {
+ if (p_port == 0)
+ return "edge";
+ else if (p_port == 1)
+ return "x";
+ return "";
+}
+
+int VisualShaderNodeVectorScalarStep::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorScalarStep::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorScalarStep::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorScalarStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = step( " + p_input_vars[0] + ", " + p_input_vars[1] + " );\n";
+}
+
+VisualShaderNodeVectorScalarStep::VisualShaderNodeVectorScalarStep() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Scalar SmoothStep
+
+String VisualShaderNodeScalarSmoothStep::get_caption() const {
+ return "ScalarSmoothStep";
+}
+
+int VisualShaderNodeScalarSmoothStep::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeScalarSmoothStep::PortType VisualShaderNodeScalarSmoothStep::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeScalarSmoothStep::get_input_port_name(int p_port) const {
+ if (p_port == 0)
+ return "edge0";
+ else if (p_port == 1)
+ return "edge1";
+ else if (p_port == 2)
+ return "x";
+ return "";
+}
+
+int VisualShaderNodeScalarSmoothStep::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScalarSmoothStep::PortType VisualShaderNodeScalarSmoothStep::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeScalarSmoothStep::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeScalarSmoothStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = smoothstep( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeScalarSmoothStep::VisualShaderNodeScalarSmoothStep() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+}
+
+////////////// Vector SmoothStep
+
+String VisualShaderNodeVectorSmoothStep::get_caption() const {
+ return "VectorSmoothStep";
+}
+
+int VisualShaderNodeVectorSmoothStep::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeVectorSmoothStep::PortType VisualShaderNodeVectorSmoothStep::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorSmoothStep::get_input_port_name(int p_port) const {
+ if (p_port == 0)
+ return "edge0";
+ else if (p_port == 1)
+ return "edge1";
+ else if (p_port == 2)
+ return "x";
+ return "";
+}
+
+int VisualShaderNodeVectorSmoothStep::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorSmoothStep::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorSmoothStep::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorSmoothStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = smoothstep( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorSmoothStep::VisualShaderNodeVectorSmoothStep() {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Vector-Scalar SmoothStep
+
+String VisualShaderNodeVectorScalarSmoothStep::get_caption() const {
+ return "VectorScalarSmoothStep";
+}
+
+int VisualShaderNodeVectorScalarSmoothStep::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeVectorScalarSmoothStep::PortType VisualShaderNodeVectorScalarSmoothStep::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_SCALAR;
+ } else if (p_port == 1) {
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorScalarSmoothStep::get_input_port_name(int p_port) const {
+ if (p_port == 0)
+ return "edge0";
+ else if (p_port == 1)
+ return "edge1";
+ else if (p_port == 2)
+ return "x";
+ return "";
+}
+
+int VisualShaderNodeVectorScalarSmoothStep::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorScalarSmoothStep::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorScalarSmoothStep::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorScalarSmoothStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = smoothstep( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorScalarSmoothStep::VisualShaderNodeVectorScalarSmoothStep() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Distance
+
+String VisualShaderNodeVectorDistance::get_caption() const {
+ return "Distance";
+}
+
+int VisualShaderNodeVectorDistance::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeVectorDistance::PortType VisualShaderNodeVectorDistance::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorDistance::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "p0";
+ } else if (p_port == 1) {
+ return "p1";
+ }
+ return "";
+}
+
+int VisualShaderNodeVectorDistance::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorDistance::PortType VisualShaderNodeVectorDistance::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeVectorDistance::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorDistance::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = distance( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n";
+}
+
+VisualShaderNodeVectorDistance::VisualShaderNodeVectorDistance() {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Refract Vector
+
+String VisualShaderNodeVectorRefract::get_caption() const {
+ return "Refract";
+}
+
+int VisualShaderNodeVectorRefract::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input_port_type(int p_port) const {
+
+ if (p_port == 2) {
+ return PORT_TYPE_SCALAR;
+ }
+
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "I";
+ } else if (p_port == 1) {
+ return "N";
+ } else if (p_port == 2) {
+ return "eta";
+ }
+ return "";
+}
+
+int VisualShaderNodeVectorRefract::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVectorRefract::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = refract( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, 0.0);
+}
+
////////////// Scalar Interp
String VisualShaderNodeScalarInterp::get_caption() const {
- return "ScalarInterp";
+ return "Mix";
}
int VisualShaderNodeScalarInterp::get_input_port_count() const {
@@ -1409,7 +2396,7 @@ VisualShaderNodeScalarInterp::VisualShaderNodeScalarInterp() {
////////////// Vector Interp
String VisualShaderNodeVectorInterp::get_caption() const {
- return "VectorInterp";
+ return "Mix";
}
int VisualShaderNodeVectorInterp::get_input_port_count() const {
@@ -1662,6 +2649,47 @@ String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualS
VisualShaderNodeScalarUniform::VisualShaderNodeScalarUniform() {
}
+////////////// Boolean Uniform
+
+String VisualShaderNodeBooleanUniform::get_caption() const {
+ return "BooleanUniform";
+}
+
+int VisualShaderNodeBooleanUniform::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeBooleanUniform::PortType VisualShaderNodeBooleanUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_BOOLEAN;
+}
+
+String VisualShaderNodeBooleanUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeBooleanUniform::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeBooleanUniform::PortType VisualShaderNodeBooleanUniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_BOOLEAN;
+}
+
+String VisualShaderNodeBooleanUniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeBooleanUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform bool " + get_uniform_name() + ";\n";
+}
+
+String VisualShaderNodeBooleanUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeBooleanUniform::VisualShaderNodeBooleanUniform() {
+}
+
////////////// Color Uniform
String VisualShaderNodeColorUniform::get_caption() const {
@@ -1924,3 +2952,203 @@ String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, Visual
VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() {
}
+
+////////////// If
+
+String VisualShaderNodeIf::get_caption() const {
+ return "If";
+}
+
+int VisualShaderNodeIf::get_input_port_count() const {
+ return 6;
+}
+
+VisualShaderNodeIf::PortType VisualShaderNodeIf::get_input_port_type(int p_port) const {
+ if (p_port == 0 || p_port == 1 || p_port == 2) {
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeIf::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "a";
+ case 1:
+ return "b";
+ case 2:
+ return "tolerance";
+ case 3:
+ return "a == b";
+ case 4:
+ return "a > b";
+ case 5:
+ return "a < b";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeIf::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeIf::PortType VisualShaderNodeIf::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeIf::get_output_port_name(int p_port) const {
+ return "result";
+}
+
+String VisualShaderNodeIf::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String code;
+ code += "\tif(abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ")<" + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[3] + ";\n";
+ code += "\t}\n";
+ code += "\telse if(" + p_input_vars[0] + "<" + p_input_vars[1] + ")\n"; // a < b
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[5] + ";\n";
+ code += "\t}\n";
+ code += "\telse\n"; // a > b (or a >= b if abs(a - b) < tolerance is false)
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[4] + ";\n";
+ code += "\t}\n";
+ return code;
+}
+
+VisualShaderNodeIf::VisualShaderNodeIf() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, CMP_EPSILON);
+ set_input_port_default_value(3, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(4, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(5, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Switch
+
+String VisualShaderNodeSwitch::get_caption() const {
+ return "Switch";
+}
+
+int VisualShaderNodeSwitch::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_BOOLEAN;
+ }
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeSwitch::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "value";
+ case 1:
+ return "true";
+ case 2:
+ return "false";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeSwitch::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeSwitch::get_output_port_name(int p_port) const {
+ return "result";
+}
+
+String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String code;
+ code += "\tif(" + p_input_vars[0] + ")\n";
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[1] + ";\n";
+ code += "\t}\n";
+ code += "\telse\n";
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[2] + ";\n";
+ code += "\t}\n";
+ return code;
+}
+
+VisualShaderNodeSwitch::VisualShaderNodeSwitch() {
+ set_input_port_default_value(0, false);
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Fresnel
+
+String VisualShaderNodeFresnel::get_caption() const {
+ return "Fresnel";
+}
+
+int VisualShaderNodeFresnel::get_input_port_count() const {
+ return 4;
+}
+
+VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR;
+ case 1:
+ return PORT_TYPE_VECTOR;
+ case 2:
+ return PORT_TYPE_BOOLEAN;
+ case 3:
+ return PORT_TYPE_SCALAR;
+ default:
+ return PORT_TYPE_VECTOR;
+ }
+}
+
+String VisualShaderNodeFresnel::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "normal";
+ case 1:
+ return "view";
+ case 2:
+ return "invert";
+ case 3:
+ return "power";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeFresnel::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeFresnel::get_output_port_name(int p_port) const {
+ return "result";
+}
+
+String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "\t" + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + p_input_vars[0] + ", " + p_input_vars[1] + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + p_input_vars[0] + ", " + p_input_vars[1] + "), 0.0, 1.0), " + p_input_vars[3] + "));";
+}
+
+VisualShaderNodeFresnel::VisualShaderNodeFresnel() {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, false);
+ set_input_port_default_value(3, 1.0);
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 27b557494a..852248b9b4 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -33,7 +33,9 @@
#include "scene/resources/visual_shader.h"
-/// CONSTANTS ///
+///////////////////////////////////////
+/// CONSTANTS
+///////////////////////////////////////
class VisualShaderNodeScalarConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeScalarConstant, VisualShaderNode)
@@ -63,6 +65,38 @@ public:
VisualShaderNodeScalarConstant();
};
+///////////////////////////////////////
+
+class VisualShaderNodeBooleanConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeBooleanConstant, VisualShaderNode)
+ bool constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_constant(bool p_value);
+ bool get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeBooleanConstant();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeColorConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode)
Color constant;
@@ -91,6 +125,8 @@ public:
VisualShaderNodeColorConstant();
};
+///////////////////////////////////////
+
class VisualShaderNodeVec3Constant : public VisualShaderNode {
GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode)
Vector3 constant;
@@ -119,6 +155,8 @@ public:
VisualShaderNodeVec3Constant();
};
+///////////////////////////////////////
+
class VisualShaderNodeTransformConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode)
Transform constant;
@@ -147,7 +185,9 @@ public:
VisualShaderNodeTransformConstant();
};
-//////////////////////////////////
+///////////////////////////////////////
+/// TEXTURES
+///////////////////////////////////////
class VisualShaderNodeTexture : public VisualShaderNode {
GDCLASS(VisualShaderNodeTexture, VisualShaderNode)
@@ -208,7 +248,7 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTexture::TextureType)
VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source)
-//////////////////////////////////
+///////////////////////////////////////
class VisualShaderNodeCubeMap : public VisualShaderNode {
GDCLASS(VisualShaderNodeCubeMap, VisualShaderNode)
@@ -254,6 +294,9 @@ public:
};
VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType)
+
+///////////////////////////////////////
+/// OPS
///////////////////////////////////////
class VisualShaderNodeScalarOp : public VisualShaderNode {
@@ -269,7 +312,8 @@ public:
OP_POW,
OP_MAX,
OP_MIN,
- OP_ATAN2
+ OP_ATAN2,
+ OP_STEP
};
protected:
@@ -313,8 +357,10 @@ public:
OP_POW,
OP_MAX,
OP_MIN,
- OP_CROSS
-
+ OP_CROSS,
+ OP_ATAN2,
+ OP_REFLECT,
+ OP_STEP
};
protected:
@@ -345,6 +391,8 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeVectorOp::Operator)
+///////////////////////////////////////
+
class VisualShaderNodeColorOp : public VisualShaderNode {
GDCLASS(VisualShaderNodeColorOp, VisualShaderNode)
@@ -358,7 +406,7 @@ public:
OP_DODGE,
OP_BURN,
OP_SOFT_LIGHT,
- OP_HARD_LIGHT,
+ OP_HARD_LIGHT
};
protected:
@@ -389,6 +437,10 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator)
+///////////////////////////////////////
+/// TRANSFORM-TRANSFORM MULTIPLICATION
+///////////////////////////////////////
+
class VisualShaderNodeTransformMult : public VisualShaderNode {
GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode)
@@ -396,6 +448,8 @@ public:
enum Operator {
OP_AxB,
OP_BxA,
+ OP_AxB_COMP,
+ OP_BxA_COMP
};
protected:
@@ -426,6 +480,10 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator)
+///////////////////////////////////////
+/// TRANSFORM-VECTOR MULTIPLICATION
+///////////////////////////////////////
+
class VisualShaderNodeTransformVecMult : public VisualShaderNode {
GDCLASS(VisualShaderNodeTransformVecMult, VisualShaderNode)
@@ -466,6 +524,8 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTransformVecMult::Operator)
///////////////////////////////////////
+/// SCALAR FUNC
+///////////////////////////////////////
class VisualShaderNodeScalarFunc : public VisualShaderNode {
GDCLASS(VisualShaderNodeScalarFunc, VisualShaderNode)
@@ -492,6 +552,18 @@ public:
FUNC_FRAC,
FUNC_SATURATE,
FUNC_NEGATE,
+ FUNC_ACOSH,
+ FUNC_ASINH,
+ FUNC_ATANH,
+ FUNC_DEGREES,
+ FUNC_EXP2,
+ FUNC_INVERSE_SQRT,
+ FUNC_LOG2,
+ FUNC_RADIANS,
+ FUNC_RECIPROCAL,
+ FUNC_ROUNDEVEN,
+ FUNC_TRUNC,
+ FUNC_ONEMINUS
};
protected:
@@ -523,6 +595,8 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeScalarFunc::Function)
///////////////////////////////////////
+/// VECTOR FUNC
+///////////////////////////////////////
class VisualShaderNodeVectorFunc : public VisualShaderNode {
GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode)
@@ -535,6 +609,35 @@ public:
FUNC_RECIPROCAL,
FUNC_RGB2HSV,
FUNC_HSV2RGB,
+ FUNC_ABS,
+ FUNC_ACOS,
+ FUNC_ACOSH,
+ FUNC_ASIN,
+ FUNC_ASINH,
+ FUNC_ATAN,
+ FUNC_ATANH,
+ FUNC_CEIL,
+ FUNC_COS,
+ FUNC_COSH,
+ FUNC_DEGREES,
+ FUNC_EXP,
+ FUNC_EXP2,
+ FUNC_FLOOR,
+ FUNC_FRAC,
+ FUNC_INVERSE_SQRT,
+ FUNC_LOG,
+ FUNC_LOG2,
+ FUNC_RADIANS,
+ FUNC_ROUND,
+ FUNC_ROUNDEVEN,
+ FUNC_SIGN,
+ FUNC_SIN,
+ FUNC_SINH,
+ FUNC_SQRT,
+ FUNC_TAN,
+ FUNC_TANH,
+ FUNC_TRUNC,
+ FUNC_ONEMINUS
};
protected:
@@ -566,6 +669,90 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeVectorFunc::Function)
///////////////////////////////////////
+/// COLOR FUNC
+///////////////////////////////////////
+
+class VisualShaderNodeColorFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeColorFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_GRAYSCALE,
+ FUNC_SEPIA
+ };
+
+protected:
+ Function func;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_function(Function p_op);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeColorFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeColorFunc::Function)
+
+///////////////////////////////////////
+/// TRANSFORM FUNC
+///////////////////////////////////////
+
+class VisualShaderNodeTransformFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_INVERSE,
+ FUNC_TRANSPOSE
+ };
+
+protected:
+ Function func;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_function(Function p_op);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTransformFunc::Function)
+
+///////////////////////////////////////
+/// DOT
+///////////////////////////////////////
class VisualShaderNodeDotProduct : public VisualShaderNode {
GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode)
@@ -587,6 +774,8 @@ public:
};
///////////////////////////////////////
+/// LENGTH
+///////////////////////////////////////
class VisualShaderNodeVectorLen : public VisualShaderNode {
GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode)
@@ -608,6 +797,337 @@ public:
};
///////////////////////////////////////
+/// DETERMINANT
+///////////////////////////////////////
+
+class VisualShaderNodeDeterminant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDeterminant, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeDeterminant();
+};
+
+///////////////////////////////////////
+/// CLAMP
+///////////////////////////////////////
+
+class VisualShaderNodeScalarClamp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarClamp, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeScalarClamp();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorClamp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorClamp, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorClamp();
+};
+
+///////////////////////////////////////
+/// DERIVATIVE FUNCTIONS
+///////////////////////////////////////
+
+class VisualShaderNodeScalarDerivativeFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarDerivativeFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_SUM,
+ FUNC_X,
+ FUNC_Y
+ };
+
+protected:
+ Function func;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_function(Function p_op);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarDerivativeFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeScalarDerivativeFunc::Function)
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorDerivativeFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorDerivativeFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_SUM,
+ FUNC_X,
+ FUNC_Y
+ };
+
+protected:
+ Function func;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_function(Function p_op);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVectorDerivativeFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorDerivativeFunc::Function)
+
+///////////////////////////////////////
+/// FACEFORWARD
+///////////////////////////////////////
+
+class VisualShaderNodeFaceForward : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeFaceForward, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeFaceForward();
+};
+
+///////////////////////////////////////
+/// OUTER PRODUCT
+///////////////////////////////////////
+
+class VisualShaderNodeOuterProduct : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeOuterProduct, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeOuterProduct();
+};
+
+///////////////////////////////////////
+/// STEP
+///////////////////////////////////////
+
+class VisualShaderNodeVectorScalarStep : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorScalarStep, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorScalarStep();
+};
+
+///////////////////////////////////////
+/// SMOOTHSTEP
+///////////////////////////////////////
+
+class VisualShaderNodeScalarSmoothStep : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarSmoothStep, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeScalarSmoothStep();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorSmoothStep : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorSmoothStep, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorSmoothStep();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorScalarSmoothStep : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorScalarSmoothStep, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorScalarSmoothStep();
+};
+
+///////////////////////////////////////
+/// DISTANCE
+///////////////////////////////////////
+
+class VisualShaderNodeVectorDistance : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorDistance, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorDistance();
+};
+
+///////////////////////////////////////
+/// REFRACT
+///////////////////////////////////////
+
+class VisualShaderNodeVectorRefract : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorRefract, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorRefract();
+};
+
+///////////////////////////////////////
+/// MIX
+///////////////////////////////////////
class VisualShaderNodeScalarInterp : public VisualShaderNode {
GDCLASS(VisualShaderNodeScalarInterp, VisualShaderNode)
@@ -650,6 +1170,8 @@ public:
};
///////////////////////////////////////
+/// COMPOSE
+///////////////////////////////////////
class VisualShaderNodeVectorCompose : public VisualShaderNode {
GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNode)
@@ -692,6 +1214,8 @@ public:
};
///////////////////////////////////////
+/// DECOMPOSE
+///////////////////////////////////////
class VisualShaderNodeVectorDecompose : public VisualShaderNode {
GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNode)
@@ -734,6 +1258,8 @@ public:
};
///////////////////////////////////////
+/// UNIFORMS
+///////////////////////////////////////
class VisualShaderNodeScalarUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeScalarUniform, VisualShaderNodeUniform)
@@ -755,6 +1281,30 @@ public:
VisualShaderNodeScalarUniform();
};
+///////////////////////////////////////
+
+class VisualShaderNodeBooleanUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeBooleanUniform, VisualShaderNodeUniform)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeBooleanUniform();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeColorUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform)
@@ -775,6 +1325,8 @@ public:
VisualShaderNodeColorUniform();
};
+///////////////////////////////////////
+
class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform)
@@ -795,6 +1347,8 @@ public:
VisualShaderNodeVec3Uniform();
};
+///////////////////////////////////////
+
class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform)
@@ -815,7 +1369,7 @@ public:
VisualShaderNodeTransformUniform();
};
-//////////////////////////////////
+///////////////////////////////////////
class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform)
@@ -867,7 +1421,7 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType)
VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault)
-//////////////////////////////////
+///////////////////////////////////////
class VisualShaderNodeCubeMapUniform : public VisualShaderNode {
GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode)
@@ -888,4 +1442,70 @@ public:
VisualShaderNodeCubeMapUniform();
};
+///////////////////////////////////////
+/// IF
+///////////////////////////////////////
+
+class VisualShaderNodeIf : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeIf, VisualShaderNode)
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeIf();
+};
+
+///////////////////////////////////////
+/// SWITCH
+///////////////////////////////////////
+
+class VisualShaderNodeSwitch : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeSwitch, VisualShaderNode)
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeSwitch();
+};
+
+///////////////////////////////////////
+/// FRESNEL
+///////////////////////////////////////
+
+class VisualShaderNodeFresnel : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeFresnel, VisualShaderNode)
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeFresnel();
+};
+
#endif // VISUAL_SHADER_NODES_H