summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp30
-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/camera_2d.cpp10
-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.cpp135
-rw-r--r--scene/2d/cpu_particles_2d.h8
-rw-r--r--scene/2d/light_2d.cpp4
-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.cpp21
-rw-r--r--scene/2d/line_2d.h4
-rw-r--r--scene/2d/mesh_instance_2d.cpp2
-rw-r--r--scene/2d/multimesh_instance_2d.cpp111
-rw-r--r--scene/2d/multimesh_instance_2d.h65
-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.cpp35
-rw-r--r--scene/2d/position_2d.cpp36
-rw-r--r--scene/2d/position_2d.h5
-rw-r--r--scene/2d/skeleton_2d.h6
-rw-r--r--scene/2d/tile_map.cpp145
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/area.cpp5
-rw-r--r--scene/3d/arvr_nodes.cpp64
-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/baked_lightmap.cpp2
-rw-r--r--scene/3d/camera.cpp99
-rw-r--r--scene/3d/camera.h13
-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.cpp131
-rw-r--r--scene/3d/cpu_particles.h6
-rw-r--r--scene/3d/gi_probe.cpp2
-rw-r--r--scene/3d/light.cpp8
-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.cpp57
-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.h5
-rw-r--r--scene/3d/voxel_light_baker.cpp6
-rw-r--r--scene/animation/animation_blend_tree.cpp4
-rw-r--r--scene/animation/animation_cache.cpp3
-rw-r--r--scene/animation/animation_node_state_machine.cpp14
-rw-r--r--scene/animation/animation_player.cpp21
-rw-r--r--scene/animation/animation_tree.cpp52
-rw-r--r--scene/animation/animation_tree_player.cpp17
-rw-r--r--scene/animation/skeleton_ik.cpp34
-rw-r--r--scene/animation/skeleton_ik.h2
-rw-r--r--scene/animation/tween.cpp600
-rw-r--r--scene/animation/tween.h1
-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.cpp243
-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.cpp206
-rw-r--r--scene/gui/color_picker.h22
-rw-r--r--scene/gui/container.cpp2
-rw-r--r--scene/gui/control.cpp209
-rw-r--r--scene/gui/control.h12
-rw-r--r--scene/gui/dialogs.cpp14
-rw-r--r--scene/gui/dialogs.h3
-rw-r--r--scene/gui/file_dialog.cpp31
-rw-r--r--scene/gui/file_dialog.h1
-rw-r--r--scene/gui/gradient_edit.cpp9
-rw-r--r--scene/gui/gradient_edit.h2
-rw-r--r--scene/gui/graph_edit.cpp6
-rw-r--r--scene/gui/graph_node.cpp4
-rw-r--r--scene/gui/item_list.cpp25
-rw-r--r--scene/gui/line_edit.cpp103
-rw-r--r--scene/gui/menu_button.cpp22
-rw-r--r--scene/gui/menu_button.h1
-rw-r--r--scene/gui/option_button.cpp51
-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/range.cpp8
-rw-r--r--scene/gui/rich_text_label.cpp42
-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.cpp628
-rw-r--r--scene/gui/text_edit.h55
-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.cpp118
-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.cpp58
-rw-r--r--scene/main/node.h18
-rw-r--r--scene/main/scene_tree.cpp18
-rw-r--r--scene/main/scene_tree.h4
-rw-r--r--scene/main/viewport.cpp91
-rw-r--r--scene/main/viewport.h5
-rw-r--r--scene/register_scene_types.cpp46
-rw-r--r--scene/resources/animation.cpp179
-rw-r--r--scene/resources/animation.h3
-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.cpp32
-rw-r--r--scene/resources/default_theme/font_hidpi.inc2
-rw-r--r--scene/resources/default_theme/font_lodpi.inc2
-rw-r--r--scene/resources/default_theme/icon_visibility.pngbin0 -> 488 bytes
-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.h16
-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/dynamic_font.h1
-rw-r--r--scene/resources/dynamic_font_stb.h1
-rw-r--r--scene/resources/environment.cpp28
-rw-r--r--scene/resources/environment.h4
-rw-r--r--scene/resources/font.cpp41
-rw-r--r--scene/resources/font.h2
-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.cpp38
-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/multimesh.cpp76
-rw-r--r--scene/resources/multimesh.h9
-rw-r--r--scene/resources/packed_scene.cpp6
-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.cpp68
-rw-r--r--scene/resources/resource_format_text.h10
-rw-r--r--scene/resources/shader.h2
-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.cpp257
-rw-r--r--scene/resources/texture.h80
-rw-r--r--scene/resources/tile_set.cpp87
-rw-r--r--scene/resources/tile_set.h21
-rw-r--r--scene/resources/visual_shader.cpp696
-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
191 files changed, 7742 insertions, 1784 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index f8384bd1e4..5bf70e12b7 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,10 @@ void AnimatedSprite::_reset_timeout() {
void AnimatedSprite::set_animation(const StringName &p_animation) {
+ ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND(frames == NULL);
+ ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1);
+
if (animation == p_animation)
return;
@@ -666,7 +683,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 +730,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/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 11846654c5..a0d74dd283 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -29,10 +29,11 @@
/*************************************************************************/
#include "camera_2d.h"
+
+#include "core/engine.h"
#include "core/math/math_funcs.h"
#include "scene/scene_string_names.h"
#include "servers/visual_server.h"
-#include <editor/editor_node.h>
void Camera2D::_update_scroll() {
@@ -44,15 +45,16 @@ void Camera2D::_update_scroll() {
return;
}
+ if (!viewport)
+ return;
+
if (current) {
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
Transform2D xform = get_camera_transform();
- if (viewport) {
- viewport->set_canvas_transform(xform);
- }
+ viewport->set_canvas_transform(xform);
Size2 screen_size = viewport->get_visible_rect().size;
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
diff --git a/scene/2d/canvas_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 05c2253a5b..a8d72bb774 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -29,15 +29,17 @@
/*************************************************************************/
#include "cpu_particles_2d.h"
-#include "particles_2d.h"
+
#include "scene/2d/canvas_item.h"
+#include "scene/2d/particles_2d.h"
#include "scene/resources/particles_material.h"
#include "servers/visual_server.h"
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) {
@@ -84,7 +86,9 @@ void CPUParticles2D::set_randomness_ratio(float p_ratio) {
void CPUParticles2D::set_use_local_coordinates(bool p_enable) {
local_coords = p_enable;
+ set_notify_transform(!p_enable);
}
+
void CPUParticles2D::set_speed_scale(float p_scale) {
speed_scale = p_scale;
@@ -323,9 +327,9 @@ void CPUParticles2D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curv
case PARAM_ANGULAR_VELOCITY: {
_adjust_curve_range(p_curve, -360, 360);
} break;
- /*case PARAM_ORBIT_VELOCITY: {
+ case PARAM_ORBIT_VELOCITY: {
_adjust_curve_range(p_curve, -500, 500);
- } break;*/
+ } break;
case PARAM_LINEAR_ACCEL: {
_adjust_curve_range(p_curve, -200, 200);
} break;
@@ -465,7 +469,7 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_CIRCLE) {
+ if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
property.usage = 0;
}
@@ -488,12 +492,6 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
property.usage = 0;
}
-
- /*
- if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
- property.usage = 0;
- }
- */
}
static uint32_t idhash(uint32_t x) {
@@ -544,6 +542,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
velocity_xform[2] = Vector2();
}
+ float system_phase = time / lifetime;
+
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -551,21 +551,26 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (!emitting && !p.active)
continue;
- float restart_time = (float(i) / float(pcount)) * lifetime;
float local_delta = p_delta;
+ // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle.
+ // While we use time in tests later on, for randomness we use the phase as done in the
+ // original shader code, and we later multiply by lifetime to get the time.
+ float restart_phase = float(i) / float(pcount);
+
if (randomness_ratio > 0.0) {
uint32_t seed = cycle;
- if (restart_time >= time) {
+ if (restart_phase >= system_phase) {
seed -= uint32_t(1);
}
seed *= uint32_t(pcount);
seed += uint32_t(i);
float random = float(idhash(seed) % uint32_t(65536)) / 65536.0;
- restart_time += randomness_ratio * random * 1.0 / float(pcount);
+ restart_phase += randomness_ratio * random * 1.0 / float(pcount);
}
- restart_time *= (1.0 - explosiveness_ratio);
+ restart_phase *= (1.0 - explosiveness_ratio);
+ float restart_time = restart_phase * lifetime;
bool restart = false;
if (time > prev_time) {
@@ -642,8 +647,9 @@ void CPUParticles2D::_particles_process(float p_delta) {
case EMISSION_SHAPE_POINT: {
//do none
} break;
- case EMISSION_SHAPE_CIRCLE: {
- p.transform[2] = Vector2(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius;
+ case EMISSION_SHAPE_SPHERE: {
+ Vector3 sphere_shape = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius;
+ p.transform[2] = Vector2(sphere_shape.x, sphere_shape.y);
} break;
case EMISSION_SHAPE_RECTANGLE: {
p.transform[2] = Vector2(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_rect_extents;
@@ -687,16 +693,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]);
}
- /*
- float tex_orbit_velocity = 0.0;
-
- if (flags[FLAG_DISABLE_Z]) {
- if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) {
- tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]);
- }
+ float tex_orbit_velocity = 0.0;
+ if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
+ tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]);
}
-*/
+
float tex_angular_velocity = 0.0;
if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
@@ -747,22 +749,19 @@ void CPUParticles2D::_particles_process(float p_delta) {
force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2();
//apply tangential acceleration;
Vector2 yx = Vector2(diff.y, diff.x);
- force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
+ force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
-#if 0
- if (flags[FLAG_DISABLE_Z]) {
-
- float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);
- if (orbit_amount != 0.0) {
- float ang = orbit_amount * DELTA * pi * 2.0;
- mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));
- TRANSFORM[3].xy -= diff.xy;
- TRANSFORM[3].xy += rot * diff.xy;
- }
+ float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
+ if (orbit_amount != 0.0) {
+ float ang = orbit_amount * local_delta * Math_PI * 2.0;
+ // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
+ // but we use -ang here to reproduce its behavior.
+ Transform2D rot = Transform2D(-ang, Vector2());
+ p.transform[2] -= diff;
+ p.transform[2] += rot.basis_xform(diff);
}
-#endif
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
p.velocity = p.velocity.normalized() * tex_linear_velocity;
}
@@ -864,11 +863,6 @@ void CPUParticles2D::_update_particle_data_buffer() {
PoolVector<Particle>::Read r = particles.read();
float *ptr = w.ptr();
- Transform2D un_transform;
- if (!local_coords) {
- un_transform = get_global_transform().affine_inverse();
- }
-
if (draw_order != DRAW_ORDER_INDEX) {
ow = particle_order.write();
order = ow.ptr();
@@ -890,7 +884,7 @@ void CPUParticles2D::_update_particle_data_buffer() {
Transform2D t = r[idx].transform;
if (!local_coords) {
- t = un_transform * t;
+ t = inv_emission_transform * t;
}
if (r[idx].active) {
@@ -965,7 +959,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 +971,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 +995,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 +1010,7 @@ void CPUParticles2D::_notification(int p_what) {
return;
}
}
+ _set_redraw(true);
if (time == 0 && pre_process_time > 0.0) {
@@ -1059,6 +1053,42 @@ void CPUParticles2D::_notification(int p_what) {
_update_particle_data_buffer();
}
+
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+
+ inv_emission_transform = get_global_transform().affine_inverse();
+
+ if (!local_coords) {
+
+ int pc = particles.size();
+
+ PoolVector<float>::Write w = particle_data.write();
+ PoolVector<Particle>::Read r = particles.read();
+ float *ptr = w.ptr();
+
+ for (int i = 0; i < pc; i++) {
+
+ Transform2D t = inv_emission_transform * r[i].transform;
+
+ if (r[i].active) {
+
+ ptr[0] = t.elements[0][0];
+ ptr[1] = t.elements[1][0];
+ ptr[2] = 0;
+ ptr[3] = t.elements[2][0];
+ ptr[4] = t.elements[0][1];
+ ptr[5] = t.elements[1][1];
+ ptr[6] = 0;
+ ptr[7] = t.elements[2][1];
+
+ } else {
+ zeromem(ptr, sizeof(float) * 8);
+ }
+
+ ptr += 13;
+ }
+ }
+ }
}
void CPUParticles2D::convert_from_particles(Node *p_particles) {
@@ -1173,15 +1203,16 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Drawing", "");
+ // No visibility_rect property contrarily to Particles2D, it's updated automatically.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
@@ -1262,12 +1293,10 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
- /*
ADD_GROUP("Orbit Velocity", "orbit_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY);
-*/
ADD_GROUP("Linear Accel", "linear_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
@@ -1323,10 +1352,12 @@ void CPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_MAX);
BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); // Unused, but exposed for consistency with 3D.
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); // Unused, but exposed for consistency with 3D.
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
- BIND_ENUM_CONSTANT(EMISSION_SHAPE_CIRCLE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_RECTANGLE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
@@ -1361,7 +1392,7 @@ CPUParticles2D::CPUParticles2D() {
set_spread(45);
set_flatness(0);
set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
- //set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_ORBIT_VELOCITY, 0);
set_param(PARAM_LINEAR_ACCEL, 0);
set_param(PARAM_ANGULAR_VELOCITY, 0);
set_param(PARAM_RADIAL_ACCEL, 0);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 23d2586331..6c83abb311 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -68,12 +68,14 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
+ FLAG_ROTATE_Y, // Unused, but exposed for consistency with 3D.
+ FLAG_DISABLE_Z, // Unused, but exposed for consistency with 3D.
FLAG_MAX
};
enum EmissionShape {
EMISSION_SHAPE_POINT,
- EMISSION_SHAPE_CIRCLE,
+ EMISSION_SHAPE_SPHERE,
EMISSION_SHAPE_RECTANGLE,
EMISSION_SHAPE_POINTS,
EMISSION_SHAPE_DIRECTED_POINTS,
@@ -116,7 +118,7 @@ private:
const Particle *particles;
bool operator()(int p_a, int p_b) const {
- return particles[p_a].time < particles[p_b].time;
+ return particles[p_a].time > particles[p_b].time;
}
};
@@ -142,6 +144,8 @@ private:
int fixed_fps;
bool fractional_delta;
+ Transform2D inv_emission_transform;
+
DrawOrder draw_order;
Ref<Texture> texture;
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index d1fce74085..7f01ff8806 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -435,10 +435,10 @@ void Light2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "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..5ba184b324 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -105,6 +105,7 @@ void Line2D::set_point_position(int i, Vector2 pos) {
}
Vector2 Line2D::get_point_position(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), Vector2());
return _points.get(i);
}
@@ -112,8 +113,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 +323,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/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
new file mode 100644
index 0000000000..ca75302163
--- /dev/null
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* multimesh_instance_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multimesh_instance_2d.h"
+
+void MultiMeshInstance2D::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_DRAW) {
+ if (multimesh.is_valid()) {
+ draw_multimesh(multimesh, texture, normal_map);
+ }
+ }
+}
+
+void MultiMeshInstance2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance2D::set_multimesh);
+ ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance2D::get_multimesh);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MultiMeshInstance2D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &MultiMeshInstance2D::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MultiMeshInstance2D::set_normal_map);
+ ClassDB::bind_method(D_METHOD("get_normal_map"), &MultiMeshInstance2D::get_normal_map);
+
+ ADD_SIGNAL(MethodInfo("texture_changed"));
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
+}
+
+void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) {
+
+ multimesh = p_multimesh;
+ update();
+}
+
+Ref<MultiMesh> MultiMeshInstance2D::get_multimesh() const {
+
+ return multimesh;
+}
+
+void MultiMeshInstance2D::set_texture(const Ref<Texture> &p_texture) {
+
+ if (p_texture == texture)
+ return;
+ texture = p_texture;
+ update();
+ emit_signal("texture_changed");
+ _change_notify("texture");
+}
+
+Ref<Texture> MultiMeshInstance2D::get_texture() const {
+
+ return texture;
+}
+
+void MultiMeshInstance2D::set_normal_map(const Ref<Texture> &p_texture) {
+
+ normal_map = p_texture;
+ update();
+}
+
+Ref<Texture> MultiMeshInstance2D::get_normal_map() const {
+
+ return normal_map;
+}
+
+Rect2 MultiMeshInstance2D::_edit_get_rect() const {
+
+ if (multimesh.is_valid()) {
+ AABB aabb = multimesh->get_aabb();
+ return Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+ }
+
+ return Node2D::_edit_get_rect();
+}
+
+MultiMeshInstance2D::MultiMeshInstance2D() {
+}
+
+MultiMeshInstance2D::~MultiMeshInstance2D() {
+}
diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h
new file mode 100644
index 0000000000..3795497183
--- /dev/null
+++ b/scene/2d/multimesh_instance_2d.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* multimesh_instance_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIMESH_INSTANCE_2D_H
+#define MULTIMESH_INSTANCE_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/multimesh.h"
+
+class MultiMeshInstance2D : public Node2D {
+ GDCLASS(MultiMeshInstance2D, Node2D);
+
+ Ref<MultiMesh> multimesh;
+
+ Ref<Texture> texture;
+ Ref<Texture> normal_map;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_multimesh(const Ref<MultiMesh> &p_multimesh);
+ Ref<MultiMesh> get_multimesh() const;
+
+ void set_texture(const Ref<Texture> &p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_normal_map(const Ref<Texture> &p_texture);
+ Ref<Texture> get_normal_map() const;
+
+ virtual Rect2 _edit_get_rect() const;
+
+ MultiMeshInstance2D();
+ ~MultiMeshInstance2D();
+};
+
+#endif // MULTIMESH_INSTANCE_2D_H
diff --git a/scene/2d/navigation_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 690f1d4b4a..2bd9e5e2a2 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -203,8 +203,8 @@ void StaticBody2D::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -217,8 +217,8 @@ void StaticBody2D::set_friction(real_t p_friction) {
real_t StaticBody2D::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -233,8 +233,8 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -247,8 +247,8 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
real_t StaticBody2D::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
@@ -630,8 +630,8 @@ void RigidBody2D::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -643,8 +643,8 @@ void RigidBody2D::set_friction(real_t p_friction) {
}
real_t RigidBody2D::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -659,8 +659,8 @@ void RigidBody2D::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -672,8 +672,8 @@ void RigidBody2D::set_bounce(real_t p_bounce) {
}
real_t RigidBody2D::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
@@ -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..f0c46a5fb7 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -33,15 +33,19 @@
#include "core/engine.h"
#include "scene/resources/texture.h"
+const float DEFAULT_GIZMO_EXTENTS = 10.0;
+
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 +70,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..ec73c02d2b 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -41,10 +41,15 @@ class Position2D : public Node2D {
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..b3b6f3175d 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -202,47 +202,27 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
Size2 s = p_sc;
Vector2 offset = p_offset;
- if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT)
- offset.y += cell_size.y;
- else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset += cell_size / 2;
- }
-
- if (s.y > s.x) {
- if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose))
- offset.y += s.y - s.x;
- } else if (s.y < s.x) {
- if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose))
- offset.x += s.x - s.y;
- }
-
if (p_cell.transpose) {
SWAP(xform.elements[0].x, xform.elements[0].y);
SWAP(xform.elements[1].x, xform.elements[1].y);
SWAP(offset.x, offset.y);
SWAP(s.x, s.y);
}
+
if (p_cell.flip_h) {
xform.elements[0].x = -xform.elements[0].x;
xform.elements[1].x = -xform.elements[1].x;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT)
- offset.x = s.x - offset.x;
- else if (tile_origin == TILE_ORIGIN_CENTER)
- offset.x = s.x - offset.x / 2;
+ offset.x = s.x - offset.x;
}
+
if (p_cell.flip_v) {
xform.elements[0].y = -xform.elements[0].y;
xform.elements[1].y = -xform.elements[1].y;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT)
- offset.y = s.y - offset.y;
- else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- offset.y += s.y;
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset.y += s.y;
- }
+ offset.y = s.y - offset.y;
}
- xform.elements[2].x += offset.x;
- xform.elements[2].y += offset.y;
+ /* For a future CheckBox to Center Texture:
+ offset += cell_size / 2 - s / 2; */
+ xform.elements[2] += offset;
}
void TileMap::update_dirty_quadrants() {
@@ -390,64 +370,26 @@ void TileMap::update_dirty_quadrants() {
rect.size.x += fp_adjust;
rect.size.y += fp_adjust;
- if (rect.size.y > rect.size.x) {
- if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose))
- tile_ofs.y += rect.size.y - rect.size.x;
- } else if (rect.size.y < rect.size.x) {
- if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose))
- tile_ofs.x += rect.size.x - rect.size.y;
- }
-
- /* rect.size.x+=fp_adjust;
- rect.size.y+=fp_adjust;*/
-
- if (c.transpose)
+ if (c.transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
+ /* For a future CheckBox to Center Texture:
+ rect.position.x += cell_size.x / 2 - rect.size.y / 2;
+ rect.position.y += cell_size.y / 2 - rect.size.x / 2;
+ } else {
+ rect.position += cell_size / 2 - rect.size / 2; */
+ }
if (c.flip_h) {
rect.size.x = -rect.size.x;
tile_ofs.x = -tile_ofs.x;
}
+
if (c.flip_v) {
rect.size.y = -rect.size.y;
tile_ofs.y = -tile_ofs.y;
}
- Vector2 center_ofs;
-
- if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
- rect.position += tile_ofs;
-
- } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
-
- rect.position += tile_ofs;
-
- if (c.transpose) {
- if (c.flip_h)
- rect.position.x -= cell_size.x;
- else
- rect.position.x += cell_size.x;
- } else {
- if (c.flip_v)
- rect.position.y -= cell_size.y;
- else
- rect.position.y += cell_size.y;
- }
-
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
-
- rect.position += tile_ofs;
-
- if (c.flip_h)
- rect.position.x -= cell_size.x / 2;
- else
- rect.position.x += cell_size.x / 2;
-
- if (c.flip_v)
- rect.position.y -= cell_size.y / 2;
- else
- rect.position.y += cell_size.y / 2;
- }
+ rect.position += tile_ofs;
Ref<Texture> normal_map = tile_set->tile_get_normal_map(c.id);
Color modulate = tile_set->tile_get_modulate(c.id);
@@ -471,7 +413,7 @@ void TileMap::update_dirty_quadrants() {
Vector2 shape_ofs = shapes[j].shape_transform.get_origin();
- _fix_cell_transform(xform, c, shape_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, shape_ofs, s);
xform *= shapes[j].shape_transform.untranslated();
@@ -491,7 +433,7 @@ void TileMap::update_dirty_quadrants() {
shape_idx++;
#ifdef DEBUG_ENABLED
} else {
- print_error("The TileSet asigned to the TileMap " + get_name() + " has an invalid convex shape.");
+ print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape.");
#endif
}
}
@@ -523,7 +465,7 @@ void TileMap::update_dirty_quadrants() {
if (navpoly.is_valid()) {
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, npoly_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, npoly_ofs, s);
int pid = navigation->navpoly_add(navpoly, nav_rel * xform);
@@ -573,7 +515,7 @@ void TileMap::update_dirty_quadrants() {
}
Transform2D navxform;
navxform.set_origin(offset.floor());
- _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s);
+ _fix_cell_transform(navxform, c, npoly_ofs, s);
vs->canvas_item_set_transform(debug_navigation_item, navxform);
vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors);
@@ -593,7 +535,7 @@ void TileMap::update_dirty_quadrants() {
Vector2 occluder_ofs = tile_set->tile_get_occluder_offset(c.id);
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, occluder_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, occluder_ofs, s);
RID orid = VS::get_singleton()->canvas_light_occluder_create();
VS::get_singleton()->canvas_light_occluder_set_transform(orid, get_global_transform() * xform);
@@ -749,7 +691,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 +718,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 +869,17 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
_make_quadrant_dirty(Q);
} else if (tile_set->tile_get_tile_mode(id) == TileSet::SINGLE_TILE) {
+
E->get().autotile_coord_x = 0;
E->get().autotile_coord_y = 0;
+ } else if (tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
+
+ if (tile_set->autotile_get_bitmask(id, Vector2(p_x, p_y)) == TileSet::BIND_CENTER) {
+ Vector2 coord = tile_set->atlastile_get_subtile_by_priority(id, this, Vector2(p_x, p_y));
+
+ E->get().autotile_coord_x = (int)coord.x;
+ E->get().autotile_coord_y = (int)coord.y;
+ }
}
}
}
@@ -1391,18 +1346,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 +1420,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 +1646,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 +1672,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 3856d3ce54..4247266e3d 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..95eec41fb2 100644
--- a/scene/3d/arvr_nodes.cpp
+++ b/scene/3d/arvr_nodes.cpp
@@ -127,7 +127,7 @@ Point2 ARVRCamera::unproject_position(const Vector3 &p_pos) const {
return res;
};
-Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
+Vector3 ARVRCamera::project_position(const Point2 &p_point, float p_z_depth) const {
// get our ARVRServer
ARVRServer *arvr_server = ARVRServer::get_singleton();
ERR_FAIL_NULL_V(arvr_server, Vector3());
@@ -135,7 +135,7 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface();
if (arvr_interface.is_null()) {
// we might be in the editor or have VR turned off, just call superclass
- return Camera::project_position(p_point);
+ return Camera::project_position(p_point, p_z_depth);
}
if (!is_inside_tree()) {
@@ -155,7 +155,7 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0;
point *= vp_size;
- Vector3 p(point.x, point.y, -get_znear());
+ Vector3 p(point.x, point.y, -p_z_depth);
return get_camera_transform().xform(p);
};
@@ -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,18 +379,18 @@ 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();
};
ARVRController::ARVRController() {
- controller_id = 0;
+ controller_id = 1;
is_active = true;
button_states = 0;
};
@@ -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,8 +525,12 @@ Plane ARVRAnchor::get_plane() const {
return plane;
};
+Ref<Mesh> ARVRAnchor::get_mesh() const {
+ return mesh;
+}
+
ARVRAnchor::ARVRAnchor() {
- anchor_id = 0;
+ anchor_id = 1;
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/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index d66e6cc83d..2b12e78158 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -173,7 +173,7 @@ void BakedLightmapData::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::AABB, "bounds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_bounds", "get_bounds");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "cell_space_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_space_transform", "get_cell_space_transform");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_subdiv", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_subdiv", "get_cell_subdiv");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "octree", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_octree", "get_octree");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data");
}
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 368cebeeab..c7d6919a2b 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();
@@ -817,6 +869,11 @@ void ClippedCamera::clear_exceptions() {
exclude.clear();
}
+float ClippedCamera::get_clip_offset() const {
+
+ return clip_offset;
+}
+
void ClippedCamera::set_clip_to_areas(bool p_clip) {
clip_to_areas = p_clip;
@@ -860,6 +917,8 @@ void ClippedCamera::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera::set_clip_to_areas);
ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera::is_clip_to_areas_enabled);
+ ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera::get_clip_offset);
+
ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera::set_clip_to_bodies);
ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera::is_clip_to_bodies_enabled);
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index a531324a85..c0a4f77435 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;
@@ -227,6 +234,8 @@ public:
void remove_exception(const Object *p_object);
void clear_exceptions();
+ float get_clip_offset() const;
+
ClippedCamera();
~ClippedCamera();
};
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 85bc2dd529..cff147ba74 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;
@@ -301,9 +302,9 @@ void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve)
case PARAM_ANGULAR_VELOCITY: {
_adjust_curve_range(p_curve, -360, 360);
} break;
- /*case PARAM_ORBIT_VELOCITY: {
+ case PARAM_ORBIT_VELOCITY: {
_adjust_curve_range(p_curve, -500, 500);
- } break;*/
+ } break;
case PARAM_LINEAR_ACCEL: {
_adjust_curve_range(p_curve, -200, 200);
} break;
@@ -460,11 +461,10 @@ void CPUParticles::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
property.usage = 0;
}
- /*
+
if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
property.usage = 0;
}
- */
}
static uint32_t idhash(uint32_t x) {
@@ -514,6 +514,8 @@ void CPUParticles::_particles_process(float p_delta) {
velocity_xform = emission_xform.basis;
}
+ float system_phase = time / lifetime;
+
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -521,21 +523,26 @@ void CPUParticles::_particles_process(float p_delta) {
if (!emitting && !p.active)
continue;
- float restart_time = (float(i) / float(pcount)) * lifetime;
float local_delta = p_delta;
+ // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle.
+ // While we use time in tests later on, for randomness we use the phase as done in the
+ // original shader code, and we later multiply by lifetime to get the time.
+ float restart_phase = float(i) / float(pcount);
+
if (randomness_ratio > 0.0) {
uint32_t seed = cycle;
- if (restart_time >= time) {
+ if (restart_phase >= system_phase) {
seed -= uint32_t(1);
}
seed *= uint32_t(pcount);
seed += uint32_t(i);
float random = float(idhash(seed) % uint32_t(65536)) / 65536.0;
- restart_time += randomness_ratio * random * 1.0 / float(pcount);
+ restart_phase += randomness_ratio * random * 1.0 / float(pcount);
}
- restart_time *= (1.0 - explosiveness_ratio);
+ restart_phase *= (1.0 - explosiveness_ratio);
+ float restart_time = restart_phase * lifetime;
bool restart = false;
if (time > prev_time) {
@@ -690,16 +697,14 @@ void CPUParticles::_particles_process(float p_delta) {
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]);
}
- /*
- float tex_orbit_velocity = 0.0;
+ float tex_orbit_velocity = 0.0;
if (flags[FLAG_DISABLE_Z]) {
-
- if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) {
- tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]);
+ if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
+ tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]);
}
}
-*/
+
float tex_angular_velocity = 0.0;
if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
@@ -753,8 +758,9 @@ void CPUParticles::_particles_process(float p_delta) {
//apply tangential acceleration;
if (flags[FLAG_DISABLE_Z]) {
- Vector3 yx = Vector3(diff.y, 0, diff.x);
- force += yx.length() > 0.0 ? (yx * Vector3(-1.0, 0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
+ Vector2 yx = Vector2(diff.y, diff.x);
+ Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized();
+ force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
} else {
Vector3 crossDiff = diff.normalized().cross(gravity.normalized());
@@ -763,18 +769,18 @@ void CPUParticles::_particles_process(float p_delta) {
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
-#if 0
if (flags[FLAG_DISABLE_Z]) {
-
- float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);
+ float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
if (orbit_amount != 0.0) {
- float ang = orbit_amount * DELTA * pi * 2.0;
- mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));
- TRANSFORM[3].xy -= diff.xy;
- TRANSFORM[3].xy += rot * diff.xy;
+ float ang = orbit_amount * local_delta * Math_PI * 2.0;
+ // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
+ // but we use -ang here to reproduce its behavior.
+ Transform2D rot = Transform2D(-ang, Vector2());
+ Vector2 rotv = rot.basis_xform(Vector2(diff.x, diff.y));
+ p.transform.origin -= Vector3(diff.x, diff.y, 0);
+ p.transform.origin += Vector3(rotv.x, rotv.y, 0);
}
}
-#endif
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
p.velocity = p.velocity.normalized() * tex_linear_velocity;
}
@@ -909,11 +915,6 @@ void CPUParticles::_update_particle_data_buffer() {
PoolVector<Particle>::Read r = particles.read();
float *ptr = w.ptr();
- Transform un_transform;
- if (!local_coords) {
- un_transform = get_global_transform().affine_inverse();
- }
-
if (draw_order != DRAW_ORDER_INDEX) {
ow = particle_order.write();
order = ow.ptr();
@@ -931,7 +932,12 @@ void CPUParticles::_update_particle_data_buffer() {
Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close
if (local_coords) {
- dir = un_transform.basis.xform(dir).normalized();
+
+ // will look different from Particles in editor as this is based on the camera in the scenetree
+ // and not the editor camera
+ dir = inv_emission_transform.xform(dir).normalized();
+ } else {
+ dir = dir.normalized();
}
SortArray<int, SortAxis> sorter;
@@ -949,7 +955,7 @@ void CPUParticles::_update_particle_data_buffer() {
Transform t = r[idx].transform;
if (!local_coords) {
- t = un_transform * t;
+ t = inv_emission_transform * t;
}
if (r[idx].active) {
@@ -1002,9 +1008,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 +1037,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 +1056,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 +1071,7 @@ void CPUParticles::_notification(int p_what) {
return;
}
}
+ _set_redraw(true);
bool processed = false;
@@ -1114,6 +1121,46 @@ void CPUParticles::_notification(int p_what) {
_update_particle_data_buffer();
}
}
+
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+
+ inv_emission_transform = get_global_transform().affine_inverse();
+
+ if (!local_coords) {
+
+ int pc = particles.size();
+
+ PoolVector<float>::Write w = particle_data.write();
+ PoolVector<Particle>::Read r = particles.read();
+ float *ptr = w.ptr();
+
+ for (int i = 0; i < pc; i++) {
+
+ Transform t = inv_emission_transform * r[i].transform;
+
+ if (r[i].active) {
+ ptr[0] = t.basis.elements[0][0];
+ ptr[1] = t.basis.elements[0][1];
+ ptr[2] = t.basis.elements[0][2];
+ ptr[3] = t.origin.x;
+ ptr[4] = t.basis.elements[1][0];
+ ptr[5] = t.basis.elements[1][1];
+ ptr[6] = t.basis.elements[1][2];
+ ptr[7] = t.origin.y;
+ ptr[8] = t.basis.elements[2][0];
+ ptr[9] = t.basis.elements[2][1];
+ ptr[10] = t.basis.elements[2][2];
+ ptr[11] = t.origin.z;
+ } else {
+ zeromem(ptr, sizeof(float) * 12);
+ }
+
+ ptr += 17;
+ }
+
+ can_update = true;
+ }
+ }
}
void CPUParticles::convert_from_particles(Node *p_particles) {
@@ -1169,7 +1216,7 @@ void CPUParticles::convert_from_particles(Node *p_particles) {
CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY);
CONVERT_PARAM(PARAM_ANGULAR_VELOCITY);
- // CONVERT_PARAM(PARAM_ORBIT_VELOCITY);
+ CONVERT_PARAM(PARAM_ORBIT_VELOCITY);
CONVERT_PARAM(PARAM_LINEAR_ACCEL);
CONVERT_PARAM(PARAM_RADIAL_ACCEL);
CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL);
@@ -1312,12 +1359,10 @@ void CPUParticles::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
- /*
ADD_GROUP("Orbit Velocity", "orbit_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY);
-*/
ADD_GROUP("Linear Accel", "linear_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
@@ -1360,7 +1405,7 @@ void CPUParticles::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
- //BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
+ BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL);
BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL);
BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL);
@@ -1374,6 +1419,7 @@ void CPUParticles::_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);
@@ -1391,7 +1437,10 @@ CPUParticles::CPUParticles() {
cycle = 0;
redraw = false;
+ set_notify_transform(true);
+
multimesh = VisualServer::get_singleton()->multimesh_create();
+ VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
set_base(multimesh);
set_emitting(true);
@@ -1411,7 +1460,7 @@ CPUParticles::CPUParticles() {
set_spread(45);
set_flatness(0);
set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
- //set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_ORBIT_VELOCITY, 0);
set_param(PARAM_LINEAR_ACCEL, 0);
set_param(PARAM_RADIAL_ACCEL, 0);
set_param(PARAM_TANGENTIAL_ACCEL, 0);
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
index b863a3cb3f..6f267102fa 100644
--- a/scene/3d/cpu_particles.h
+++ b/scene/3d/cpu_particles.h
@@ -53,7 +53,7 @@ public:
PARAM_INITIAL_LINEAR_VELOCITY,
PARAM_ANGULAR_VELOCITY,
- //PARAM_ORBIT_VELOCITY,
+ PARAM_ORBIT_VELOCITY,
PARAM_LINEAR_ACCEL,
PARAM_RADIAL_ACCEL,
PARAM_TANGENTIAL_ACCEL,
@@ -116,7 +116,7 @@ private:
const Particle *particles;
bool operator()(int p_a, int p_b) const {
- return particles[p_a].time < particles[p_b].time;
+ return particles[p_a].time > particles[p_b].time;
}
};
@@ -142,6 +142,8 @@ private:
int fixed_fps;
bool fractional_delta;
+ Transform inv_emission_transform;
+
volatile bool can_update;
DrawOrder draw_order;
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index c491275f00..414e932f61 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -539,7 +539,7 @@ void GIProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
ADD_PROPERTY(PropertyInfo(Variant::INT, "dynamic_range", PROPERTY_HINT_RANGE, "1,16,1"), "set_dynamic_range", "get_dynamic_range");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bias", PROPERTY_HINT_RANGE, "0,4,0.001"), "set_bias", "get_bias");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "normal_bias", PROPERTY_HINT_RANGE, "0,4,0.001"), "set_normal_bias", "get_normal_bias");
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index cf1af918f7..2e64872616 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 {
@@ -248,8 +249,8 @@ void Light::_bind_methods() {
ADD_GROUP("Light", "light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_ENERGY);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disable,Indirect,All"), "set_bake_mode", "get_bake_mode");
@@ -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..3624e04434 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -192,8 +192,8 @@ void StaticBody::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -206,8 +206,8 @@ void StaticBody::set_friction(real_t p_friction) {
real_t StaticBody::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -222,8 +222,8 @@ void StaticBody::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -236,8 +236,8 @@ void StaticBody::set_bounce(real_t p_bounce) {
real_t StaticBody::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
@@ -636,8 +636,8 @@ void RigidBody::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -649,8 +649,8 @@ void RigidBody::set_friction(real_t p_friction) {
}
real_t RigidBody::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -665,8 +665,8 @@ void RigidBody::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -677,8 +677,8 @@ void RigidBody::set_bounce(real_t p_bounce) {
physics_material_override->set_bounce(p_bounce);
}
real_t RigidBody::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
}
@@ -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..3b924e0454 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;
@@ -87,6 +89,7 @@ class GeometryInstance : public VisualInstance {
public:
enum Flags {
FLAG_USE_BAKED_LIGHT = VS::INSTANCE_FLAG_USE_BAKED_LIGHT,
+ FLAG_DRAW_NEXT_FRAME_IF_VISIBLE = VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE,
FLAG_MAX = VS::INSTANCE_FLAG_MAX,
};
@@ -137,6 +140,8 @@ public:
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;
+ void set_custom_aabb(AABB aabb);
+
GeometryInstance();
};
diff --git a/scene/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..20a09696e1 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++) {
@@ -1047,7 +1049,7 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node
return CONNECTION_ERROR_NO_INPUT;
}
- if (!nodes.has(p_input_node)) {
+ if (p_input_node == p_output_node) {
return CONNECTION_ERROR_SAME_NODE;
}
diff --git a/scene/animation/animation_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..a2411743d4 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
@@ -598,6 +599,9 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
{
Ref<AnimationNode> node = states[p_name].node;
+
+ ERR_FAIL_COND(node.is_null());
+
node->disconnect("tree_changed", this, "_tree_changed");
}
@@ -960,7 +964,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..54f0fdc26a 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -316,7 +316,7 @@ String AnimationNode::get_caption() const {
void AnimationNode::add_input(const String &p_name) {
//root nodes can't add inputs
- ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL)
+ ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL);
Input input;
ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
input.name = p_name;
@@ -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..5c3e123ac3 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);
@@ -402,7 +404,7 @@ void AnimationTreePlayer::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
ERR_EXPLAIN("AnimationTreePlayer has been deprecated. Use AnimationTree instead.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
if (!processing) {
//make sure that a previous process state was not saved
@@ -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..c70e58564f 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -34,10 +34,14 @@
void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) {
+ // Add a new pending command and reference it
pending_commands.push_back(PendingCommand());
PendingCommand &cmd = pending_commands.back()->get();
+ // Update the command with the target key
cmd.key = p_key;
+
+ // Determine command argument count
int &count = cmd.args;
if (p_arg10.get_type() != Variant::NIL)
count = 10;
@@ -59,6 +63,9 @@ void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const
count = 2;
else if (p_arg1.get_type() != Variant::NIL)
count = 1;
+
+ // Add the specified arguments to the command
+ // TODO: Make this a switch statement?
if (count > 0)
cmd.arg[0] = p_arg1;
if (count > 1)
@@ -83,10 +90,14 @@ void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const
void Tween::_process_pending_commands() {
+ // For each pending command...
for (List<PendingCommand>::Element *E = pending_commands.front(); E; E = E->next()) {
+ // Get the command
PendingCommand &cmd = E->get();
Variant::CallError err;
+
+ // Grab all of the arguments for the command
Variant *arg[10] = {
&cmd.arg[0],
&cmd.arg[1],
@@ -99,16 +110,20 @@ void Tween::_process_pending_commands() {
&cmd.arg[8],
&cmd.arg[9],
};
+
+ // Execute the command (and retrieve any errors)
this->call(cmd.key, (const Variant **)arg, cmd.args, err);
}
+
+ // Clear the pending commands
pending_commands.clear();
}
bool Tween::_set(const StringName &p_name, const Variant &p_value) {
+ // Set the correct attribute based on the given name
String name = p_name;
-
- if (name == "playback/speed" || name == "speed") { //bw compatibility
+ if (name == "playback/speed" || name == "speed") { // Backwards compatibility
set_speed_scale(p_value);
} else if (name == "playback/active") {
@@ -122,69 +137,78 @@ bool Tween::_set(const StringName &p_name, const Variant &p_value) {
bool Tween::_get(const StringName &p_name, Variant &r_ret) const {
+ // Get the correct attribute based on the given name
String name = p_name;
-
- if (name == "playback/speed") { //bw compatibility
-
+ if (name == "playback/speed") { // Backwards compatibility
r_ret = speed_scale;
- } else if (name == "playback/active") {
+ } else if (name == "playback/active") {
r_ret = is_active();
- } else if (name == "playback/repeat") {
+ } else if (name == "playback/repeat") {
r_ret = is_repeat();
}
-
return true;
}
void Tween::_get_property_list(List<PropertyInfo> *p_list) const {
-
+ // Add the property info for the Tween object
p_list->push_back(PropertyInfo(Variant::BOOL, "playback/active", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::BOOL, "playback/repeat", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::REAL, "playback/speed", PROPERTY_HINT_RANGE, "-64,64,0.01"));
}
void Tween::_notification(int p_what) {
-
+ // What notification did we receive?
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
-
+ // Are we not already active?
if (!is_active()) {
- //make sure that a previous process state was not saved
- //only process if "processing" is set
+ // Make sure that a previous process state was not saved
+ // Only process if "processing" is set
set_physics_process_internal(false);
set_process_internal(false);
}
} break;
- case NOTIFICATION_READY: {
+ case NOTIFICATION_READY: {
+ // Do nothing
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
+ // Are we processing during physics time?
if (tween_process_mode == TWEEN_PROCESS_PHYSICS)
+ // Do nothing since we aren't aligned with physics when we should be
break;
+ // Should we update?
if (is_active())
+ // Update the tweens
_tween_process(get_process_delta_time());
} break;
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ // Are we processing during 'regular' time?
if (tween_process_mode == TWEEN_PROCESS_IDLE)
+ // Do nothing since we whould only process during idle time
break;
+ // Should we update?
if (is_active())
+ // Update the tweens
_tween_process(get_physics_process_delta_time());
} break;
- case NOTIFICATION_EXIT_TREE: {
+ case NOTIFICATION_EXIT_TREE: {
+ // We've left the tree. Stop all tweens
stop_all();
} break;
}
}
void Tween::_bind_methods() {
-
+ // Bind getters and setters
ClassDB::bind_method(D_METHOD("is_active"), &Tween::is_active);
ClassDB::bind_method(D_METHOD("set_active", "active"), &Tween::set_active);
@@ -197,6 +221,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tween_process_mode", "mode"), &Tween::set_tween_process_mode);
ClassDB::bind_method(D_METHOD("get_tween_process_mode"), &Tween::get_tween_process_mode);
+ // Bind the various Tween control methods
ClassDB::bind_method(D_METHOD("start"), &Tween::start);
ClassDB::bind_method(D_METHOD("reset", "object", "key"), &Tween::reset, DEFVAL(""));
ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all);
@@ -211,6 +236,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("tell"), &Tween::tell);
ClassDB::bind_method(D_METHOD("get_runtime"), &Tween::get_runtime);
+ // Bind interpolation and follow methods
ClassDB::bind_method(D_METHOD("interpolate_property", "object", "property", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::interpolate_property, DEFVAL(0));
ClassDB::bind_method(D_METHOD("interpolate_method", "object", "method", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::interpolate_method, DEFVAL(0));
ClassDB::bind_method(D_METHOD("interpolate_callback", "object", "duration", "callback", "arg1", "arg2", "arg3", "arg4", "arg5"), &Tween::interpolate_callback, DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()));
@@ -220,17 +246,22 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("targeting_property", "object", "property", "initial", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_property, DEFVAL(0));
ClassDB::bind_method(D_METHOD("targeting_method", "object", "method", "initial", "initial_method", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_method, DEFVAL(0));
+ // Add the Tween signals
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 the properties and tie them to the getters and setters
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
+ // Bind Idle vs Physics process
BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE);
+ // Bind the Transition type constants
BIND_ENUM_CONSTANT(TRANS_LINEAR);
BIND_ENUM_CONSTANT(TRANS_SINE);
BIND_ENUM_CONSTANT(TRANS_QUINT);
@@ -243,6 +274,7 @@ void Tween::_bind_methods() {
BIND_ENUM_CONSTANT(TRANS_BOUNCE);
BIND_ENUM_CONSTANT(TRANS_BACK);
+ // Bind the easing constants
BIND_ENUM_CONSTANT(EASE_IN);
BIND_ENUM_CONSTANT(EASE_OUT);
BIND_ENUM_CONSTANT(EASE_IN_OUT);
@@ -251,27 +283,30 @@ void Tween::_bind_methods() {
Variant &Tween::_get_initial_val(InterpolateData &p_data) {
+ // What type of data are we interpolating?
switch (p_data.type) {
case INTER_PROPERTY:
case INTER_METHOD:
case FOLLOW_PROPERTY:
case FOLLOW_METHOD:
+ // Simply use the given initial value
return p_data.initial_val;
case TARGETING_PROPERTY:
case TARGETING_METHOD: {
-
+ // Get the object that is being targeted
Object *object = ObjectDB::get_instance(p_data.target_id);
ERR_FAIL_COND_V(object == NULL, p_data.initial_val);
+ // Are we targeting a property or a method?
static Variant initial_val;
if (p_data.type == TARGETING_PROPERTY) {
-
+ // Get the property from the target object
bool valid = false;
initial_val = object->get_indexed(p_data.target_key, &valid);
ERR_FAIL_COND_V(!valid, p_data.initial_val);
} else {
-
+ // Call the method and get the initial value from it
Variant::CallError error;
initial_val = object->call(p_data.target_key[0], NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val);
@@ -280,64 +315,75 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) {
}
case INTER_CALLBACK:
+ // Callback does not have a special initial value
break;
}
+ // If we've made it here, just return the delta value as the initial value
return p_data.delta_val;
}
Variant &Tween::_get_delta_val(InterpolateData &p_data) {
+ // What kind of data are we interpolating?
switch (p_data.type) {
case INTER_PROPERTY:
case INTER_METHOD:
+ // Simply return the given delta value
return p_data.delta_val;
case FOLLOW_PROPERTY:
case FOLLOW_METHOD: {
-
+ // We're following an object, so grab that instance
Object *target = ObjectDB::get_instance(p_data.target_id);
ERR_FAIL_COND_V(target == NULL, p_data.initial_val);
+ // We want to figure out the final value
Variant final_val;
-
if (p_data.type == FOLLOW_PROPERTY) {
-
+ // Read the property as-is
bool valid = false;
final_val = target->get_indexed(p_data.target_key, &valid);
ERR_FAIL_COND_V(!valid, p_data.initial_val);
} else {
-
+ // We're looking at a method. Call the method on the target object
Variant::CallError error;
final_val = target->call(p_data.target_key[0], NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val);
}
- // convert INT to REAL is better for interpolaters
+ // If we're looking at an INT value, instead convert it to a REAL
+ // This is better for interpolation
if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t();
+
+ // Calculate the delta based on the initial value and the final value
_calc_delta_val(p_data.initial_val, final_val, p_data.delta_val);
return p_data.delta_val;
}
case TARGETING_PROPERTY:
case TARGETING_METHOD: {
-
+ // Grab the initial value from the data to calculate delta
Variant initial_val = _get_initial_val(p_data);
- // convert INT to REAL is better for interpolaters
+
+ // If we're looking at an INT value, instead convert it to a REAL
+ // This is better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
- //_calc_delta_val(p_data.initial_val, p_data.final_val, p_data.delta_val);
+ // Calculate the delta based on the initial value and the final value
_calc_delta_val(initial_val, p_data.final_val, p_data.delta_val);
return p_data.delta_val;
}
case INTER_CALLBACK:
+ // Callbacks have no special delta
break;
}
+ // If we've made it here, use the initial value as the delta
return p_data.initial_val;
}
Variant Tween::_run_equation(InterpolateData &p_data) {
-
+ // Get the initial and delta values from the data
Variant &initial_val = _get_initial_val(p_data);
Variant &delta_val = _get_delta_val(p_data);
Variant result;
@@ -345,48 +391,59 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
#define APPLY_EQUATION(element) \
r.element = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, i.element, d.element, p_data.duration);
+ // What type of data are we interpolating?
switch (initial_val.get_type()) {
case Variant::BOOL:
+ // Run the boolean specific equation (checking if it is at least 0.5)
result = (_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, initial_val, delta_val, p_data.duration)) >= 0.5;
break;
case Variant::INT:
+ // Run the integer specific equation
result = (int)_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (int)initial_val, (int)delta_val, p_data.duration);
break;
case Variant::REAL:
+ // Run the REAL specific equation
result = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (real_t)initial_val, (real_t)delta_val, p_data.duration);
break;
case Variant::VECTOR2: {
+ // Get vectors for initial and delta values
Vector2 i = initial_val;
Vector2 d = delta_val;
Vector2 r;
+ // Execute the equation and mutate the r vector
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(x);
APPLY_EQUATION(y);
-
result = r;
} break;
case Variant::VECTOR3: {
+ // Get vectors for initial and delta values
Vector3 i = initial_val;
Vector3 d = delta_val;
Vector3 r;
+ // Execute the equation and mutate the r vector
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(x);
APPLY_EQUATION(y);
APPLY_EQUATION(z);
-
result = r;
} break;
case Variant::BASIS: {
+ // Get the basis for initial and delta values
Basis i = initial_val;
Basis d = delta_val;
Basis r;
+ // Execute the equation on all the basis and mutate the r basis
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(elements[0][0]);
APPLY_EQUATION(elements[0][1]);
APPLY_EQUATION(elements[0][2]);
@@ -396,55 +453,63 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
APPLY_EQUATION(elements[2][0]);
APPLY_EQUATION(elements[2][1]);
APPLY_EQUATION(elements[2][2]);
-
result = r;
} break;
case Variant::TRANSFORM2D: {
+ // Get the transforms for initial and delta values
Transform2D i = initial_val;
Transform2D d = delta_val;
Transform2D r;
+ // Execute the equation on the transforms and mutate the r transform
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(elements[0][0]);
APPLY_EQUATION(elements[0][1]);
APPLY_EQUATION(elements[1][0]);
APPLY_EQUATION(elements[1][1]);
APPLY_EQUATION(elements[2][0]);
APPLY_EQUATION(elements[2][1]);
-
result = r;
} break;
case Variant::QUAT: {
+ // Get the quaternian for the initial and delta values
Quat i = initial_val;
Quat d = delta_val;
Quat r;
+ // Execute the equation on the quaternian values and mutate the r quaternian
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(x);
APPLY_EQUATION(y);
APPLY_EQUATION(z);
APPLY_EQUATION(w);
-
result = r;
} break;
case Variant::AABB: {
+ // Get the AABB's for the initial and delta values
AABB i = initial_val;
AABB d = delta_val;
AABB r;
+ // Execute the equation for the position and size of the AABB's and mutate the r AABB
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(position.x);
APPLY_EQUATION(position.y);
APPLY_EQUATION(position.z);
APPLY_EQUATION(size.x);
APPLY_EQUATION(size.y);
APPLY_EQUATION(size.z);
-
result = r;
} break;
case Variant::TRANSFORM: {
+ // Get the transforms for the initial and delta values
Transform i = initial_val;
Transform d = delta_val;
Transform r;
+ // Execute the equation for each of the transforms and their origin and mutate the r transform
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(basis.elements[0][0]);
APPLY_EQUATION(basis.elements[0][1]);
APPLY_EQUATION(basis.elements[0][2]);
@@ -457,40 +522,45 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
APPLY_EQUATION(origin.x);
APPLY_EQUATION(origin.y);
APPLY_EQUATION(origin.z);
-
result = r;
} break;
case Variant::COLOR: {
+ // Get the Color for initial and delta value
Color i = initial_val;
Color d = delta_val;
Color r;
+ // Apply the equation on the Color RGBA, and mutate the r color
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(r);
APPLY_EQUATION(g);
APPLY_EQUATION(b);
APPLY_EQUATION(a);
-
result = r;
} break;
default: {
+ // If unknown, just return the initial value
result = initial_val;
} break;
};
#undef APPLY_EQUATION
-
+ // Return the result that was computed
return result;
}
bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) {
+ // Get the object we want to apply the new value to
Object *object = ObjectDB::get_instance(p_data.id);
ERR_FAIL_COND_V(object == NULL, false);
+ // What kind of data are we mutating?
switch (p_data.type) {
case INTER_PROPERTY:
case FOLLOW_PROPERTY:
case TARGETING_PROPERTY: {
+ // Simply set the property on the object
bool valid = false;
object->set_indexed(p_data.key, value, &valid);
return valid;
@@ -499,85 +569,112 @@ bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) {
case INTER_METHOD:
case FOLLOW_METHOD:
case TARGETING_METHOD: {
+ // We want to call the method on the target object
Variant::CallError error;
+
+ // Do we have a non-nil value passed in?
if (value.get_type() != Variant::NIL) {
+ // Pass it as an argument to the function call
Variant *arg[1] = { &value };
object->call(p_data.key[0], (const Variant **)arg, 1, error);
} else {
+ // Don't pass any argument
object->call(p_data.key[0], NULL, 0, error);
}
+ // Did we get an error from the function call?
if (error.error == Variant::CallError::CALL_OK)
return true;
return false;
}
case INTER_CALLBACK:
+ // Nothing to apply for a callback
break;
};
+ // No issues found!
return true;
}
void Tween::_tween_process(float p_delta) {
-
+ // Process all of the pending commands
_process_pending_commands();
+ // If the scale is 0, make no progress on the tweens
if (speed_scale == 0)
return;
- p_delta *= speed_scale;
+ // Update the delta and whether we are pending an update
+ p_delta *= speed_scale;
pending_update++;
- // if repeat and all interpolates was finished then reset all interpolates
- bool all_finished = true;
- if (repeat) {
+ // Are we repeating the interpolations?
+ if (repeat) {
+ // For each interpolation...
+ bool repeats_finished = true;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the data from it
InterpolateData &data = E->get();
+ // Is not finished?
if (!data.finish) {
- all_finished = false;
+ // We aren't finished yet, no need to check the rest
+ repeats_finished = false;
break;
}
}
- if (all_finished)
+ // If we are all finished, we can reset all of the tweens
+ if (repeats_finished)
reset_all();
}
- all_finished = true;
+ // Are all of the tweens complete?
+ bool all_finished = true;
+
+ // For each tween we wish to interpolate...
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // Get the data from it
InterpolateData &data = E->get();
+
+ // Track if we hit one that isn't finished yet
all_finished = all_finished && data.finish;
+ // Is the data not active or already finished? No need to go any further
if (!data.active || data.finish)
continue;
+ // Get the target object for this interpolation
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+ // Are we still delaying this tween?
bool prev_delaying = data.elapsed <= data.delay;
data.elapsed += p_delta;
if (data.elapsed < data.delay)
continue;
else if (prev_delaying) {
-
+ // We can apply the tween's value to the data and emit that the tween has started
_apply_tween_value(data, data.initial_val);
emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false));
}
+ // Are we at the end of the tween?
if (data.elapsed > (data.delay + data.duration)) {
-
+ // Set the elapsed time to the end and mark this one as finished
data.elapsed = data.delay + data.duration;
data.finish = true;
}
+ // Are we interpolating a callback?
if (data.type == INTER_CALLBACK) {
+ // Is the tween completed?
if (data.finish) {
+ // Are we calling this callback deferred or immediately?
if (data.call_deferred) {
-
+ // Run the deferred function callback, applying the correct number of arguments
switch (data.args) {
case 0:
object->call_deferred(data.key[0]);
@@ -599,6 +696,7 @@ void Tween::_tween_process(float p_delta) {
break;
}
} else {
+ // Call the function directly with the arguments
Variant::CallError error;
Variant *arg[5] = {
&data.arg[0],
@@ -611,98 +709,111 @@ void Tween::_tween_process(float p_delta) {
}
}
} else {
+ // We can apply the value directly
Variant result = _run_equation(data);
_apply_tween_value(data, result);
+
+ // Emit that the tween has taken a step
emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result);
}
+ // Is the tween now finished?
if (data.finish) {
+ // Set it to the final value directly
_apply_tween_value(data, data.final_val);
+
+ // Mark the tween as completed and emit the signal
data.elapsed = 0;
emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false));
- // not repeat mode, remove completed action
+
+ // If we are not repeating the tween, remove it
if (!repeat)
call_deferred("_remove_by_uid", data.uid);
- } else if (!repeat)
+ } else if (!repeat) {
+ // Check whether all tweens are finished
all_finished = all_finished && data.finish;
+ }
}
+ // One less update left to go
pending_update--;
- if (all_finished)
+ // If all tweens are completed, we no longer need to be active
+ if (all_finished) {
set_active(false);
+ emit_signal("tween_all_completed");
+ }
}
void Tween::set_tween_process_mode(TweenProcessMode p_mode) {
-
tween_process_mode = p_mode;
}
Tween::TweenProcessMode Tween::get_tween_process_mode() const {
-
return tween_process_mode;
}
bool Tween::is_active() const {
-
return is_processing_internal() || is_physics_processing_internal();
}
void Tween::set_active(bool p_active) {
-
+ // Do nothing if it's the same active mode that we currently are
if (is_active() == p_active)
return;
+ // Depending on physics or idle, set processing
switch (tween_process_mode) {
-
case TWEEN_PROCESS_IDLE: set_process_internal(p_active); break;
case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_active); break;
}
}
bool Tween::is_repeat() const {
-
return repeat;
}
void Tween::set_repeat(bool p_repeat) {
-
repeat = p_repeat;
}
void Tween::set_speed_scale(float p_speed) {
-
speed_scale = p_speed;
}
float Tween::get_speed_scale() const {
-
return speed_scale;
}
bool Tween::start() {
+ // Are there any pending updates?
if (pending_update != 0) {
+ // Start the tweens after deferring
call_deferred("start");
return true;
}
+ // We want to be activated
set_active(true);
return true;
}
bool Tween::reset(Object *p_object, StringName p_key) {
-
+ // Find all interpolations that use the same object and target string
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target object
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+ // Do we have the correct object and key?
if (object == p_object && (data.concatenated_key == p_key || p_key == "")) {
-
+ // Reset the tween to the initial state
data.elapsed = 0;
data.finish = false;
+
+ // Also apply the initial state if there isn't a delay
if (data.delay == 0)
_apply_tween_value(data, data.initial_val);
}
@@ -712,13 +823,15 @@ bool Tween::reset(Object *p_object, StringName p_key) {
}
bool Tween::reset_all() {
-
+ // Go through all interpolations
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target data and set it back to the initial state
InterpolateData &data = E->get();
data.elapsed = 0;
data.finish = false;
+
+ // If there isn't a delay, apply the value to the object
if (data.delay == 0)
_apply_tween_value(data, data.initial_val);
}
@@ -727,15 +840,19 @@ bool Tween::reset_all() {
}
bool Tween::stop(Object *p_object, StringName p_key) {
-
+ // Find the tween that has the given target object and string key
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // Get the object the tween is targeting
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+
+ // Is this the correct object and does it have the given key?
if (object == p_object && (data.concatenated_key == p_key || p_key == ""))
+ // Disable the tween
data.active = false;
}
pending_update--;
@@ -743,12 +860,13 @@ bool Tween::stop(Object *p_object, StringName p_key) {
}
bool Tween::stop_all() {
-
+ // We no longer need to be active since all tweens have been stopped
set_active(false);
+ // For each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Simply set it inactive
InterpolateData &data = E->get();
data.active = false;
}
@@ -757,16 +875,20 @@ bool Tween::stop_all() {
}
bool Tween::resume(Object *p_object, StringName p_key) {
-
+ // We need to be activated
+ // TODO: What if no tween is found??
set_active(true);
+ // Find the tween that uses the given target object and string key
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Grab the object
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+
+ // If the object and string key match, activate it
if (object == p_object && (data.concatenated_key == p_key || p_key == ""))
data.active = true;
}
@@ -775,12 +897,14 @@ bool Tween::resume(Object *p_object, StringName p_key) {
}
bool Tween::resume_all() {
-
+ // Set ourselves active so we can process tweens
+ // TODO: What if there are no tweens? We get set to active for no reason!
set_active(true);
+ // For each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Simply grab it and set it to active
InterpolateData &data = E->get();
data.active = true;
}
@@ -789,35 +913,46 @@ bool Tween::resume_all() {
}
bool Tween::remove(Object *p_object, StringName p_key) {
+ // If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("remove", p_object, p_key);
return true;
}
+
+ // For each interpolation...
List<List<InterpolateData>::Element *> for_removal;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target object
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+
+ // If the target object and string key match, queue it for removal
if (object == p_object && (data.concatenated_key == p_key || p_key == "")) {
for_removal.push_back(E);
}
}
+
+ // For each interpolation we wish to remove...
for (List<List<InterpolateData>::Element *>::Element *E = for_removal.front(); E; E = E->next()) {
+ // Erase it
interpolates.erase(E->get());
}
return true;
}
void Tween::_remove_by_uid(int uid) {
+ // If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("_remove_by_uid", uid);
return;
}
+ // Find the interpolation that matches the given UID
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
if (uid == E->get().uid) {
+ // It matches, erase it and stop looking
E->erase();
break;
}
@@ -826,49 +961,61 @@ void Tween::_remove_by_uid(int uid) {
void Tween::_push_interpolate_data(InterpolateData &p_data) {
pending_update++;
+
+ // Add the new interpolation
p_data.uid = ++uid;
interpolates.push_back(p_data);
+
pending_update--;
}
bool Tween::remove_all() {
-
+ // If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("remove_all");
return true;
}
+ // We no longer need to be active
set_active(false);
+
+ // Clear out all interpolations and reset the uid
interpolates.clear();
uid = 0;
+
return true;
}
bool Tween::seek(real_t p_time) {
-
+ // Go through each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target data
InterpolateData &data = E->get();
+ // Update the elapsed data to be set to the target time
data.elapsed = p_time;
- if (data.elapsed < data.delay) {
+ // Are we at the end?
+ if (data.elapsed < data.delay) {
+ // There is still time left to go
data.finish = false;
continue;
} else if (data.elapsed >= (data.delay + data.duration)) {
-
- data.finish = true;
+ // We are past the end of it, set the elapsed time to the end and mark as finished
data.elapsed = (data.delay + data.duration);
+ data.finish = true;
} else {
+ // We are not finished with this interpolation yet
data.finish = false;
}
+ // If we are a callback, do nothing special
if (data.type == INTER_CALLBACK) {
continue;
}
+ // Run the equation on the data and apply the value
Variant result = _run_equation(data);
-
_apply_tween_value(data, result);
}
pending_update--;
@@ -876,13 +1023,16 @@ bool Tween::seek(real_t p_time) {
}
real_t Tween::tell() const {
-
+ // We want to grab the position of the furthest along tween
pending_update++;
real_t pos = 0;
- for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // For each interpolation...
+ for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // Get the data and figure out if it's position is further along than the previous ones
const InterpolateData &data = E->get();
if (data.elapsed > pos)
+ // Save it if so
pos = data.elapsed;
}
pending_update--;
@@ -890,55 +1040,63 @@ real_t Tween::tell() const {
}
real_t Tween::get_runtime() const {
-
+ // If the tween isn't moving, it'll last forever
if (speed_scale == 0) {
return INFINITY;
}
pending_update++;
+
+ // For each interpolation...
real_t runtime = 0;
for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the tween data and see if it's runtime is greater than the previous tweens
const InterpolateData &data = E->get();
real_t t = data.delay + data.duration;
if (t > runtime)
+ // This is the longest running tween
runtime = t;
}
pending_update--;
+ // Adjust the runtime for the current speed scale
return runtime / speed_scale;
}
bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val) {
+ // Get the initial, final, and delta values
const Variant &initial_val = p_initial_val;
const Variant &final_val = p_final_val;
Variant &delta_val = p_delta_val;
+ // What kind of data are we interpolating?
switch (initial_val.get_type()) {
case Variant::BOOL:
- //delta_val = p_final_val;
- delta_val = (int)p_final_val - (int)p_initial_val;
- break;
-
+ // We'll treat booleans just like integers
case Variant::INT:
+ // Compute the integer delta
delta_val = (int)final_val - (int)initial_val;
break;
case Variant::REAL:
+ // Convert to REAL and find the delta
delta_val = (real_t)final_val - (real_t)initial_val;
break;
case Variant::VECTOR2:
+ // Convert to Vectors and find the delta
delta_val = final_val.operator Vector2() - initial_val.operator Vector2();
break;
case Variant::VECTOR3:
+ // Convert to Vectors and find the delta
delta_val = final_val.operator Vector3() - initial_val.operator Vector3();
break;
case Variant::BASIS: {
+ // Build a new basis which is the delta between the initial and final values
Basis i = initial_val;
Basis f = final_val;
delta_val = Basis(f.elements[0][0] - i.elements[0][0],
@@ -953,6 +1111,7 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
} break;
case Variant::TRANSFORM2D: {
+ // Build a new transform which is the difference between the initial and final values
Transform2D i = initial_val;
Transform2D f = final_val;
Transform2D d = Transform2D();
@@ -964,15 +1123,21 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
d[2][1] = f.elements[2][1] - i.elements[2][1];
delta_val = d;
} break;
+
case Variant::QUAT:
+ // Convert to quaternianls and find the delta
delta_val = final_val.operator Quat() - initial_val.operator Quat();
break;
+
case Variant::AABB: {
+ // Build a new AABB and use the new position and sizes to make a delta
AABB i = initial_val;
AABB f = final_val;
delta_val = AABB(f.position - i.position, f.size - i.size);
} break;
+
case Variant::TRANSFORM: {
+ // Build a new transform which is the difference between the initial and final values
Transform i = initial_val;
Transform f = final_val;
Transform d;
@@ -991,124 +1156,157 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
delta_val = d;
} break;
+
case Variant::COLOR: {
+ // Make a new color which is the difference between each the color's RGBA attributes
Color i = initial_val;
Color f = final_val;
delta_val = Color(f.r - i.r, f.g - i.g, f.b - i.b, f.a - i.a);
} break;
default:
+ // TODO: Should move away from a 'magic string'?
ERR_PRINT("Invalid param type, except(int/real/vector2/vector/matrix/matrix32/quat/aabb/transform/color)");
return false;
};
return true;
}
-bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
- if (pending_update != 0) {
- _add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
- }
- p_property = p_property.get_as_property_path();
-
- if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames());
-
- // convert INT to REAL is better for interpolaters
- if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
- if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
-
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
- ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
- ERR_FAIL_COND_V(p_delay < 0, false);
+bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
- bool prop_valid = false;
- p_object->get_indexed(p_property.get_subnames(), &prop_valid);
- ERR_FAIL_COND_V(!prop_valid, false);
+ // TODO: Add initialization+implementation for remaining interpolation types
+ // TODO: Fix this method's organization to take advantage of the type
+ // Make a new interpolation data
InterpolateData data;
data.active = true;
- data.type = INTER_PROPERTY;
+ data.type = p_interpolation_type;
data.finish = false;
data.elapsed = 0;
+ // Validate and apply interpolation data
+
+ // Give it the object
+ ERR_EXPLAIN("Invalid object provided to Tween!");
+ ERR_FAIL_COND_V(p_object == NULL, false); // Is the object real
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false); // Is the object a valid instance?
data.id = p_object->get_instance_id();
- data.key = p_property.get_subnames();
- data.concatenated_key = p_property.get_concatenated_subnames();
+
+ // Validate the initial and final values
+ ERR_EXPLAIN("Initial value type does not match final value type!"); // TODO: Print both types to make debugging easier
+ ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false); // Do the initial and final value types match?
data.initial_val = p_initial_val;
data.final_val = p_final_val;
+
+ // Check the Duration
+ ERR_EXPLAIN("Only non-negative duration values allowed in Tweens!");
+ ERR_FAIL_COND_V(p_duration < 0, false); // Is the tween duration non-negative
data.duration = p_duration;
+
+ // Tween Delay
+ ERR_EXPLAIN("Only non-negative delay values allowed in Tweens!");
+ ERR_FAIL_COND_V(p_delay < 0, false); // Is the delay non-negative?
+ data.delay = p_delay;
+
+ // Transition type
+ ERR_EXPLAIN("Invalid transition type provided to Tween");
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false); // Is the transition type valid
data.trans_type = p_trans_type;
+
+ // Easing type
+ ERR_EXPLAIN("Invalid easing type provided to Tween");
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false); // Is the easing type valid
data.ease_type = p_ease_type;
- data.delay = p_delay;
+ // Is the property defined?
+ if (p_property) {
+ // Check that the object actually contains the given property
+ bool prop_valid = false;
+ p_object->get_indexed(p_property->get_subnames(), &prop_valid);
+ ERR_EXPLAIN("Tween target object has no property named: " + p_property->get_concatenated_subnames());
+ ERR_FAIL_COND_V(!prop_valid, false);
+
+ data.key = p_property->get_subnames();
+ data.concatenated_key = p_property->get_concatenated_subnames();
+ }
+
+ // Is the method defined?
+ if (p_method) {
+ // Does the object even have the requested method?
+ ERR_EXPLAIN("Tween target object has no method named: " + *p_method); // TODO: Fix this error message
+ ERR_FAIL_COND_V(!p_object->has_method(*p_method), false);
+
+ data.key.push_back(*p_method);
+ data.concatenated_key = *p_method;
+ }
+
+ // Is there not a valid delta?
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
return false;
+ // Add this interpolation to the total
_push_interpolate_data(data);
return true;
}
-bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are busy updating, call this function again later
if (pending_update != 0) {
- _add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ _add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
- // convert INT to REAL is better for interpolaters
- if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
- if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
- ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
- ERR_FAIL_COND_V(p_delay < 0, false);
+ // Get the property from the node path
+ p_property = p_property.get_as_property_path();
- ERR_EXPLAIN("Object has no method named: %s" + p_method);
- ERR_FAIL_COND_V(!p_object->has_method(p_method), false);
+ // If no initial value given, grab the initial value from the object
+ // TODO: Is this documented? This is very useful and removes a lot of clutter from tweens!
+ if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames());
- InterpolateData data;
- data.active = true;
- data.type = INTER_METHOD;
- data.finish = false;
- data.elapsed = 0;
+ // Convert any integers into REALs as they are better for interpolation
+ if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
- data.id = p_object->get_instance_id();
- data.key.push_back(p_method);
- data.concatenated_key = p_method;
- data.initial_val = p_initial_val;
- data.final_val = p_final_val;
- data.duration = p_duration;
- data.trans_type = p_trans_type;
- data.ease_type = p_ease_type;
- data.delay = p_delay;
+ // Build the interpolation data
+ bool result = _build_interpolation(INTER_PROPERTY, p_object, &p_property, NULL, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ return result;
+}
- if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
- return false;
+bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are busy updating, call this function again later
+ if (pending_update != 0) {
+ _add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ return true;
+ }
- _push_interpolate_data(data);
- return true;
+ // Convert any integers into REALs as they are better for interpolation
+ if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+
+ // Build the interpolation data
+ bool result = _build_interpolation(INTER_METHOD, p_object, NULL, &p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ return result;
}
bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
-
+ // If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
return true;
}
+ // Check that the target object is valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
+
+ // Duration cannot be negative
ERR_FAIL_COND_V(p_duration < 0, false);
+ // Check whether the object even has the callback
ERR_EXPLAIN("Object has no callback named: %s" + p_callback);
ERR_FAIL_COND_V(!p_object->has_method(p_callback), false);
+ // Build a new InterpolationData
InterpolateData data;
data.active = true;
data.type = INTER_CALLBACK;
@@ -1116,12 +1314,14 @@ bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_c
data.call_deferred = false;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key.push_back(p_callback);
data.concatenated_key = p_callback;
data.duration = p_duration;
data.delay = 0;
+ // Add arguments to the interpolation
int args = 0;
if (p_arg5.get_type() != Variant::NIL)
args = 5;
@@ -1143,23 +1343,30 @@ bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_c
data.arg[3] = p_arg4;
data.arg[4] = p_arg5;
+ // Add the new interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
-
+ // If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_deferred_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
return true;
}
+
+ // Check that the target object is valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
+
+ // No negative durations allowed
ERR_FAIL_COND_V(p_duration < 0, false);
+ // Confirm the callback exists on the object
ERR_EXPLAIN("Object has no callback named: %s" + p_callback);
ERR_FAIL_COND_V(!p_object->has_method(p_callback), false);
+ // Create a new InterpolateData for the callback
InterpolateData data;
data.active = true;
data.type = INTER_CALLBACK;
@@ -1167,12 +1374,14 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S
data.call_deferred = true;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key.push_back(p_callback);
data.concatenated_key = p_callback;
data.duration = p_duration;
data.delay = 0;
+ // Collect arguments for the callback
int args = 0;
if (p_arg5.get_type() != Variant::NIL)
args = 5;
@@ -1194,32 +1403,46 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S
data.arg[3] = p_arg4;
data.arg[4] = p_arg5;
+ // Add the new interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("follow_property", p_object, p_property, p_initial_val, p_target, p_target_property, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
+
+ // Get the two properties from their paths
p_property = p_property.get_as_property_path();
p_target_property = p_target_property.get_as_property_path();
+ // If no initial value is given, grab it from the source object
+ // TODO: Is this documented? It's really helpful for decluttering tweens
if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames());
- // convert INT to REAL is better for interpolaters
+ // Convert initial INT values to REAL as they are better for interpolation
if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ // Confirm the source and target objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_target == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_target), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure transition and easing types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Confirm the source and target objects have the desired properties
bool prop_valid = false;
p_object->get_indexed(p_property.get_subnames(), &prop_valid);
ERR_FAIL_COND_V(!prop_valid, false);
@@ -1228,16 +1451,20 @@ bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_ini
Variant target_val = p_target->get_indexed(p_target_property.get_subnames(), &target_prop_valid);
ERR_FAIL_COND_V(!target_prop_valid, false);
- // convert INT to REAL is better for interpolaters
+ // Convert target INT to REAL since it is better for interpolation
if (target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
+
+ // Verify that the target value and initial value are the same type
ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+ // Create a new InterpolateData
InterpolateData data;
data.active = true;
data.type = FOLLOW_PROPERTY;
data.finish = false;
data.elapsed = 0;
+ // Give the InterpolateData it's configuration
data.id = p_object->get_instance_id();
data.key = p_property.get_subnames();
data.concatenated_key = p_property.get_concatenated_subnames();
@@ -1249,46 +1476,59 @@ bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_ini
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Add the interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("follow_method", p_object, p_method, p_initial_val, p_target, p_target_method, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
- // convert INT to REAL is better for interpolaters
+ // Convert initial INT values to REAL as they are better for interpolation
if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ // Verify the source and target objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_target == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_target), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure that the transition and ease types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Confirm both objects have the target methods
ERR_EXPLAIN("Object has no method named: %s" + p_method);
ERR_FAIL_COND_V(!p_object->has_method(p_method), false);
ERR_EXPLAIN("Target has no method named: %s" + p_target_method);
ERR_FAIL_COND_V(!p_target->has_method(p_target_method), false);
+ // Call the method to get the target value
Variant::CallError error;
Variant target_val = p_target->call(p_target_method, NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, false);
- // convert INT to REAL is better for interpolaters
+ // Convert target INT values to REAL as they are better for interpolation
if (target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+ // Make the new InterpolateData for the method follow
InterpolateData data;
data.active = true;
data.type = FOLLOW_METHOD;
data.finish = false;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key.push_back(p_method);
data.concatenated_key = p_method;
@@ -1300,31 +1540,41 @@ bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initi
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Add the new interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
-
+ // If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("targeting_property", p_object, p_property, p_initial, p_initial_property, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
+ // Grab the target property and the target property
p_property = p_property.get_as_property_path();
p_initial_property = p_initial_property.get_as_property_path();
- // convert INT to REAL is better for interpolaters
+ // Convert the initial INT values to REAL as they are better for Interpolation
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+ // Verify both objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_initial), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure transition and easing types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Ensure the initial and target properties exist on their objects
bool prop_valid = false;
p_object->get_indexed(p_property.get_subnames(), &prop_valid);
ERR_FAIL_COND_V(!prop_valid, false);
@@ -1333,16 +1583,18 @@ bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_
Variant initial_val = p_initial->get_indexed(p_initial_property.get_subnames(), &initial_prop_valid);
ERR_FAIL_COND_V(!initial_prop_valid, false);
- // convert INT to REAL is better for interpolaters
+ // Convert the initial INT value to REAL as it is better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+ // Build the InterpolateData object
InterpolateData data;
data.active = true;
data.type = TARGETING_PROPERTY;
data.finish = false;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key = p_property.get_subnames();
data.concatenated_key = p_property.get_concatenated_subnames();
@@ -1355,49 +1607,64 @@ bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Ensure there is a valid delta
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
return false;
+ // Add the interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("targeting_method", p_object, p_method, p_initial, p_initial_method, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
- // convert INT to REAL is better for interpolaters
+
+ // Convert final INT values to REAL as they are better for interpolation
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+ // Make sure the given objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_initial), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure transition and easing types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Make sure both objects have the given method
ERR_EXPLAIN("Object has no method named: %s" + p_method);
ERR_FAIL_COND_V(!p_object->has_method(p_method), false);
ERR_EXPLAIN("Initial Object has no method named: %s" + p_initial_method);
ERR_FAIL_COND_V(!p_initial->has_method(p_initial_method), false);
+ // Call the method to get the initial value
Variant::CallError error;
Variant initial_val = p_initial->call(p_initial_method, NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, false);
- // convert INT to REAL is better for interpolaters
+ // Convert initial INT values to REAL as they aer better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+ // Build the new InterpolateData object
InterpolateData data;
data.active = true;
data.type = TARGETING_METHOD;
data.finish = false;
data.elapsed = 0;
+ // Configure the data
data.id = p_object->get_instance_id();
data.key.push_back(p_method);
data.concatenated_key = p_method;
@@ -1410,16 +1677,17 @@ bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_in
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Ensure there is a valid delta
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
return false;
+ // Add the interpolation
_push_interpolate_data(data);
return true;
}
Tween::Tween() {
-
- //String autoplay;
+ // Initialize tween attributes
tween_process_mode = TWEEN_PROCESS_IDLE;
repeat = false;
speed_scale = 1;
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 6fe3bffdbe..64ce099ecd 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -135,6 +135,7 @@ private:
void _tween_process(float p_delta);
void _remove_by_uid(int uid);
void _push_interpolate_data(InterpolateData &p_data);
+ bool _build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
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..fadf5e432c 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,8 @@ void BaseButton::set_pressed(bool p_pressed) {
if (p_pressed) {
_unpress_group();
}
+ _toggled(status.pressed);
+
update();
}
@@ -379,7 +253,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,10 +323,17 @@ Control::FocusMode BaseButton::get_enabled_focus_mode() const {
return enabled_focus_mode;
}
-void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
+void BaseButton::set_keep_pressed_outside(bool p_on) {
- if (shortcut.is_null() == p_shortcut.is_null())
- return;
+ 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) {
shortcut = p_shortcut;
set_process_unhandled_input(shortcut.is_valid());
@@ -470,11 +351,10 @@ void BaseButton::_unhandled_input(Ref<InputEvent> p_event) {
return; //ignore because of modal window
if (is_toggle_mode()) {
- set_pressed(!is_pressed());
- emit_signal("toggled", is_pressed());
+ set_pressed(!is_pressed()); // Also calls _toggled() internally.
}
- emit_signal("pressed");
+ _pressed();
}
}
@@ -531,6 +411,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 +434,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 +452,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..b6abfdd6a8 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) {
@@ -95,6 +93,28 @@ void ColorPicker::set_focus_on_line_edit() {
void ColorPicker::_update_controls() {
+ const char *rgb[3] = { "R", "G", "B" };
+ const char *hsv[3] = { "H", "S", "V" };
+
+ if (hsv_mode_enabled) {
+ for (int i = 0; i < 3; i++)
+ labels[i]->set_text(hsv[i]);
+ } else {
+ for (int i = 0; i < 3; i++)
+ labels[i]->set_text(rgb[i]);
+ }
+
+ if (hsv_mode_enabled) {
+ set_raw_mode(false);
+ btn_raw->set_disabled(true);
+ } else if (raw_mode_enabled) {
+ set_hsv_mode(false);
+ btn_hsv->set_disabled(true);
+ } else {
+ btn_raw->set_disabled(false);
+ btn_hsv->set_disabled(false);
+ }
+
if (edit_alpha) {
values[3]->show();
scroll[3]->show();
@@ -106,7 +126,7 @@ void ColorPicker::_update_controls() {
}
}
-void ColorPicker::set_pick_color(const Color &p_color) {
+void ColorPicker::set_pick_color(const Color &p_color, bool p_update_sliders) {
color = p_color;
if (color != last_hsv) {
@@ -119,7 +139,7 @@ void ColorPicker::set_pick_color(const Color &p_color) {
if (!is_inside_tree())
return;
- _update_color();
+ _update_color(p_update_sliders);
}
void ColorPicker::set_edit_alpha(bool p_show) {
@@ -144,11 +164,18 @@ void ColorPicker::_value_changed(double) {
if (updating)
return;
- for (int i = 0; i < 4; i++) {
- color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0);
+ if (hsv_mode_enabled) {
+ color.set_hsv(scroll[0]->get_value() / 360.0,
+ scroll[1]->get_value() / 100.0,
+ scroll[2]->get_value() / 100.0,
+ scroll[3]->get_value() / 100.0);
+ } else {
+ for (int i = 0; i < 4; i++) {
+ color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0);
+ }
}
- set_pick_color(color);
+ set_pick_color(color, false);
emit_signal("color_changed", color);
}
@@ -169,22 +196,40 @@ void ColorPicker::_html_entered(const String &p_html) {
emit_signal("color_changed", color);
}
-void ColorPicker::_update_color() {
+void ColorPicker::_update_color(bool p_update_sliders) {
updating = true;
- for (int i = 0; i < 4; i++) {
- if (raw_mode_enabled) {
- scroll[i]->set_step(0.01);
- scroll[i]->set_max(100);
- if (i == 3)
- scroll[i]->set_max(1);
- scroll[i]->set_value(color.components[i]);
+ if (p_update_sliders) {
+
+ if (hsv_mode_enabled) {
+ for (int i = 0; i < 4; i++) {
+ scroll[i]->set_step(0.1);
+ }
+
+ scroll[0]->set_max(360);
+ scroll[0]->set_value(h * 360.0);
+ scroll[1]->set_max(100);
+ scroll[1]->set_value(s * 100.0);
+ scroll[2]->set_max(100);
+ scroll[2]->set_value(v * 100.0);
+ scroll[3]->set_max(100);
+ scroll[3]->set_value(color.components[3] * 100.0);
} else {
- scroll[i]->set_step(1);
- const float byte_value = color.components[i] * 255.0;
- scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1);
- scroll[i]->set_value(byte_value);
+ for (int i = 0; i < 4; i++) {
+ if (raw_mode_enabled) {
+ scroll[i]->set_step(0.01);
+ scroll[i]->set_max(100);
+ if (i == 3)
+ scroll[i]->set_max(1);
+ scroll[i]->set_value(color.components[i]);
+ } else {
+ scroll[i]->set_step(1);
+ const float byte_value = color.components[i] * 255.0;
+ scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1);
+ scroll[i]->set_value(byte_value);
+ }
+ }
}
}
@@ -274,13 +319,33 @@ PoolColorArray ColorPicker::get_presets() const {
return arr;
}
+void ColorPicker::set_hsv_mode(bool p_enabled) {
+
+ if (hsv_mode_enabled == p_enabled || raw_mode_enabled)
+ return;
+ hsv_mode_enabled = p_enabled;
+ if (btn_hsv->is_pressed() != p_enabled)
+ btn_hsv->set_pressed(p_enabled);
+
+ if (!is_inside_tree())
+ return;
+
+ _update_controls();
+ _update_color();
+}
+
+bool ColorPicker::is_hsv_mode() const {
+
+ return hsv_mode_enabled;
+}
+
void ColorPicker::set_raw_mode(bool p_enabled) {
- if (raw_mode_enabled == p_enabled)
+ if (raw_mode_enabled == p_enabled || hsv_mode_enabled)
return;
raw_mode_enabled = p_enabled;
- if (btn_mode->is_pressed() != p_enabled)
- btn_mode->set_pressed(p_enabled);
+ if (btn_raw->is_pressed() != p_enabled)
+ btn_raw->set_pressed(p_enabled);
if (!is_inside_tree())
return;
@@ -469,7 +534,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,16 +630,47 @@ 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);
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
+ ClassDB::bind_method(D_METHOD("set_hsv_mode", "mode"), &ColorPicker::set_hsv_mode);
+ ClassDB::bind_method(D_METHOD("is_hsv_mode"), &ColorPicker::is_hsv_mode);
ClassDB::bind_method(D_METHOD("set_raw_mode", "mode"), &ColorPicker::set_raw_mode);
ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode);
ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
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);
@@ -596,8 +692,11 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hsv_mode"), "set_hsv_mode", "is_hsv_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::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")));
@@ -610,9 +709,12 @@ ColorPicker::ColorPicker() :
updating = true;
edit_alpha = true;
text_is_constructor = false;
+ hsv_mode_enabled = false;
raw_mode_enabled = false;
deferred_mode_enabled = false;
changing_color = false;
+ presets_enabled = true;
+ presets_visible = true;
screen = NULL;
HBoxContainer *hb_smpl = memnew(HBoxContainer);
@@ -658,13 +760,12 @@ ColorPicker::ColorPicker() :
VBoxContainer *vbr = memnew(VBoxContainer);
add_child(vbr);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
- const char *lt[4] = { "R", "G", "B", "A" };
for (int i = 0; i < 4; i++) {
HBoxContainer *hbc = memnew(HBoxContainer);
- labels[i] = memnew(Label(lt[i]));
+ labels[i] = memnew(Label());
labels[i]->set_custom_minimum_size(Size2(get_constant("label_width"), 0));
labels[i]->set_v_size_flags(SIZE_SHRINK_CENTER);
hbc->add_child(labels[i]);
@@ -688,14 +789,20 @@ ColorPicker::ColorPicker() :
vbr->add_child(hbc);
}
+ labels[3]->set_text("A");
HBoxContainer *hhb = memnew(HBoxContainer);
vbr->add_child(hhb);
- btn_mode = memnew(CheckButton);
- hhb->add_child(btn_mode);
- btn_mode->set_text(TTR("Raw Mode"));
- btn_mode->connect("toggled", this, "set_raw_mode");
+ btn_hsv = memnew(CheckButton);
+ hhb->add_child(btn_hsv);
+ btn_hsv->set_text(TTR("HSV"));
+ btn_hsv->connect("toggled", this, "set_hsv_mode");
+
+ btn_raw = memnew(CheckButton);
+ hhb->add_child(btn_raw);
+ btn_raw->set_text(TTR("Raw"));
+ btn_raw->connect("toggled", this, "set_raw_mode");
text_type = memnew(Button);
hhb->add_child(text_type);
@@ -725,18 +832,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 +866,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);
+ } break;
+ case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: {
- 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);
+ 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 +943,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 +975,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..94640be4f0 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,10 +53,13 @@ private:
Control *w_edit;
TextureRect *sample;
TextureRect *preset;
+ HBoxContainer *preset_container;
+ HSeparator *preset_separator;
Button *bt_add_preset;
List<Color> presets;
ToolButton *btn_pick;
- CheckButton *btn_mode;
+ CheckButton *btn_hsv;
+ CheckButton *btn_raw;
HSlider *scroll[4];
SpinBox *values[4];
Label *labels[4];
@@ -67,16 +71,19 @@ private:
Color color;
bool raw_mode_enabled;
+ bool hsv_mode_enabled;
bool deferred_mode_enabled;
bool updating;
bool changing_color;
+ bool presets_enabled;
+ bool presets_visible;
float h, s, v;
Color last_hsv;
void _html_entered(const String &p_html);
void _value_changed(double);
void _update_controls();
- void _update_color();
+ void _update_color(bool p_update_sliders = true);
void _update_presets();
void _update_text_value();
void _text_type_toggled();
@@ -101,19 +108,28 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
- void set_pick_color(const Color &p_color);
+ void set_pick_color(const Color &p_color, bool p_update_sliders = true);
Color get_pick_color() const;
void add_preset(const Color &p_color);
void erase_preset(const Color &p_color);
PoolColorArray get_presets() const;
+ void set_hsv_mode(bool p_enabled);
+ bool is_hsv_mode() const;
+
void set_raw_mode(bool p_enabled);
bool is_raw_mode() const;
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..c9cf5f8308 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 its 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..0845b56828 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -29,22 +29,23 @@
/*************************************************************************/
#include "control.h"
-#include "core/project_settings.h"
-#include "scene/main/canvas_layer.h"
-#include "scene/main/viewport.h"
-#include "servers/visual_server.h"
#include "core/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/print_string.h"
+#include "core/project_settings.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
+#include "scene/main/canvas_layer.h"
+#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
+#include "servers/visual_server.h"
+
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
#endif
-#include <stdio.h>
Dictionary Control::_edit_get_state() const {
@@ -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,18 @@ 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;
+ ERR_FAIL_COND(parent_rect_size.x == 0.0);
+ ERR_FAIL_COND(parent_rect_size.y == 0.0);
+
+ 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 +1767,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 +1797,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 +1843,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 +2697,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 +2720,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 +2765,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 +2901,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 +2917,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/dialogs.cpp b/scene/gui/dialogs.cpp
index 89f3d509d0..4da11b671e 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -406,12 +406,20 @@ void AcceptDialog::set_hide_on_ok(bool p_hide) {
hide_on_ok = p_hide;
}
-
bool AcceptDialog::get_hide_on_ok() const {
return hide_on_ok;
}
+void AcceptDialog::set_autowrap(bool p_autowrap) {
+
+ label->set_autowrap(p_autowrap);
+}
+bool AcceptDialog::has_autowrap() {
+
+ return label->has_autowrap();
+}
+
void AcceptDialog::register_text_enter(Node *p_line_edit) {
ERR_FAIL_NULL(p_line_edit);
@@ -530,6 +538,8 @@ void AcceptDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_custom_action"), &AcceptDialog::_custom_action);
ClassDB::bind_method(D_METHOD("set_text", "text"), &AcceptDialog::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &AcceptDialog::get_text);
+ ClassDB::bind_method(D_METHOD("set_autowrap", "autowrap"), &AcceptDialog::set_autowrap);
+ ClassDB::bind_method(D_METHOD("has_autowrap"), &AcceptDialog::has_autowrap);
ADD_SIGNAL(MethodInfo("confirmed"));
ADD_SIGNAL(MethodInfo("custom_action", PropertyInfo(Variant::STRING, "action")));
@@ -537,6 +547,7 @@ void AcceptDialog::_bind_methods() {
ADD_GROUP("Dialog", "dialog");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
}
bool AcceptDialog::swap_ok_cancel = false;
@@ -555,7 +566,6 @@ AcceptDialog::AcceptDialog() {
label->set_anchor(MARGIN_BOTTOM, ANCHOR_END);
label->set_begin(Point2(margin, margin));
label->set_end(Point2(-margin, -button_margin - 10));
- //label->set_autowrap(true);
add_child(label);
hbc = memnew(HBoxContainer);
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 4b89ac54c5..c1a7f26a85 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -145,6 +145,9 @@ public:
void set_text(String p_text);
String get_text() const;
+ void set_autowrap(bool p_autowrap);
+ bool has_autowrap();
+
AcceptDialog();
~AcceptDialog();
};
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 059e59ea21..bc8dcf0e82 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -48,8 +48,9 @@ void FileDialog::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- refresh->set_icon(get_icon("reload"));
dir_up->set_icon(get_icon("parent_folder"));
+ refresh->set_icon(get_icon("reload"));
+ show_hidden->set_icon(get_icon("toggle_hidden"));
}
if (p_what == NOTIFICATION_POPUP_HIDE) {
@@ -86,7 +87,9 @@ void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
_dir_entered("..");
} break;
- default: { handled = false; }
+ default: {
+ handled = false;
+ }
}
if (handled)
@@ -391,20 +394,19 @@ void FileDialog::update_file_list() {
List<String> files;
List<String> dirs;
- bool isdir;
- bool ishidden;
- bool show_hidden = show_hidden_files;
+ bool is_dir;
+ bool is_hidden;
String item;
- while ((item = dir_access->get_next(&isdir)) != "") {
+ while ((item = dir_access->get_next(&is_dir)) != "") {
if (item == "." || item == "..")
continue;
- ishidden = dir_access->current_is_hidden();
+ is_hidden = dir_access->current_is_hidden();
- if (show_hidden || !ishidden) {
- if (!isdir)
+ if (show_hidden_files || !is_hidden) {
+ if (!is_dir)
files.push_back(item);
else
dirs.push_back(item);
@@ -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) {
@@ -875,6 +873,13 @@ FileDialog::FileDialog() {
refresh->connect("pressed", this, "_update_file_list");
hbc->add_child(refresh);
+ show_hidden = memnew(ToolButton);
+ show_hidden->set_toggle_mode(true);
+ show_hidden->set_pressed(is_showing_hidden_files());
+ show_hidden->set_tooltip(RTR("Toggle Hidden Files"));
+ show_hidden->connect("toggled", this, "set_show_hidden_files");
+ hbc->add_child(show_hidden);
+
drives = memnew(OptionButton);
hbc->add_child(drives);
drives->connect("item_selected", this, "_select_drive");
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 85edac0b12..9f7ea1a2f2 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -90,6 +90,7 @@ private:
ToolButton *dir_up;
ToolButton *refresh;
+ ToolButton *show_hidden;
Vector<String> filters;
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index cfbc9d6d18..75f5f79873 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -29,10 +29,17 @@
/*************************************************************************/
#include "gradient_edit.h"
+
#include "core/os/keyboard.h"
-#include "editor/editor_scale.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_scale.h"
#define SPACING (3 * EDSCALE)
+#define POINT_WIDTH (8 * EDSCALE)
+#else
+#define SPACING 3
+#define POINT_WIDTH 8
+#endif
GradientEdit::GradientEdit() {
grabbed = -1;
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 662278a17b..6f31107729 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -36,8 +36,6 @@
#include "scene/resources/default_theme/theme_data.h"
#include "scene/resources/gradient.h"
-#define POINT_WIDTH (8 * EDSCALE)
-
class GradientEdit : public Control {
GDCLASS(GradientEdit, Control);
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 e8692d56d2..6463ee5ad5 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -159,9 +159,7 @@ void GraphNode::_resort() {
fit_child_in_rect(c, r);
cache_y.push_back(vofs + size.y * 0.5);
- if (vofs > 0)
- vofs += sep;
- vofs += size.y;
+ vofs += size.y + sep;
}
update();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 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 bf6833e512..da6bff8ab8 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"
@@ -43,7 +44,7 @@
static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return !is_symbol(c);
}
void LineEdit::_gui_input(Ref<InputEvent> p_event) {
@@ -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: {
@@ -504,7 +539,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (handled) {
accept_event();
- } else if (!k->get_alt() && !k->get_command()) {
+ } else if (!k->get_command()) {
if (k->get_unicode() >= 32 && k->get_scancode() != KEY_DELETE) {
if (editable) {
@@ -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..872402e9e1 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();
+ }
}
}
@@ -81,44 +86,27 @@ void OptionButton::_focused(int p_which) {
void OptionButton::_selected(int p_which) {
- int selid = -1;
- for (int i = 0; i < popup->get_item_count(); i++) {
-
- bool is_clicked = popup->get_item_id(i) == p_which;
- if (is_clicked) {
- selid = i;
- break;
- }
- }
-
- if (selid == -1 && p_which >= 0 && p_which < popup->get_item_count()) {
- _select(p_which, true);
- } else {
-
- ERR_FAIL_COND(selid == -1);
-
- _select(selid, true);
- }
+ _select(p_which, true);
}
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 +119,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 +321,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->connect("id_pressed", this, "_selected");
+ popup->set_notify_transform(true);
+ popup->set_allow_search(true);
+ popup->connect("index_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/range.cpp b/scene/gui/range.cpp
index c24e62c8cb..d00acaf08a 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -63,7 +63,7 @@ void Range::Shared::emit_value_changed() {
void Range::_changed_notify(const char *p_what) {
- emit_signal("changed", shared->val);
+ emit_signal("changed");
update();
_change_notify(p_what);
}
@@ -79,6 +79,7 @@ void Range::Shared::emit_changed(const char *p_what) {
}
void Range::set_value(double p_val) {
+
if (shared->step > 0)
p_val = Math::round(p_val / shared->step) * shared->step;
@@ -303,22 +304,27 @@ bool Range::is_ratio_exp() const {
}
void Range::set_allow_greater(bool p_allow) {
+
shared->allow_greater = p_allow;
}
bool Range::is_greater_allowed() const {
+
return shared->allow_greater;
}
void Range::set_allow_lesser(bool p_allow) {
+
shared->allow_lesser = p_allow;
}
bool Range::is_lesser_allowed() const {
+
return shared->allow_lesser;
}
Range::Range() {
+
shared = memnew(Shared);
shared->min = 0;
shared->max = 100;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 8968923b2c..8891f679ee 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -307,6 +307,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
switch (it->type) {
+ case ITEM_ALIGN: {
+
+ ItemAlign *align_it = static_cast<ItemAlign *>(it);
+
+ align = align_it->align;
+
+ } break;
case ITEM_TEXT: {
ItemText *text = static_cast<ItemText *>(it);
@@ -592,7 +599,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
//assign actual widths
for (int i = 0; i < table->columns.size(); i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand)
+ if (table->columns[i].expand && total_ratio > 0)
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
table->total_width += table->columns[i].width + hseparation;
}
@@ -711,7 +718,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
} break;
- default: {}
+ default: {
+ }
}
Item *itp = it;
@@ -752,6 +760,8 @@ void RichTextLabel::_scroll_changed(double) {
else
scroll_following = false;
+ scroll_updated = true;
+
update();
}
@@ -777,7 +787,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) {
@@ -900,13 +909,21 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
- if (selection.click)
+ if (!underline_meta || selection.click)
return CURSOR_ARROW;
if (main->first_invalid_line < main->lines.size())
return CURSOR_ARROW; //invalid
- return get_default_cursor_shape();
+ int line = 0;
+ Item *item = NULL;
+
+ ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line);
+
+ if (item && ((RichTextLabel *)(this))->_find_meta(item, NULL))
+ return CURSOR_POINTING_HAND;
+
+ return CURSOR_ARROW;
}
void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
@@ -1136,8 +1153,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;
}
}
@@ -1401,9 +1418,13 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
if (p_enter)
current = p_item;
- if (p_ensure_newline && current_frame->lines[current_frame->lines.size() - 1].from) {
- _invalidate_current_line(current_frame);
- current_frame->lines.resize(current_frame->lines.size() + 1);
+ if (p_ensure_newline) {
+ Item *from = current_frame->lines[current_frame->lines.size() - 1].from;
+ // only create a new line for Item types that generate content/layout, ignore those that represent formatting/styling
+ if (from && from->type != ITEM_FONT && from->type != ITEM_COLOR && from->type != ITEM_UNDERLINE && from->type != ITEM_STRIKETHROUGH) {
+ _invalidate_current_line(current_frame);
+ current_frame->lines.resize(current_frame->lines.size() + 1);
+ }
}
if (current_frame->lines[current_frame->lines.size() - 1].from == NULL) {
@@ -1996,7 +2017,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;
}
@@ -2326,6 +2347,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 2bf2364873..36d7dad1d3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -50,7 +50,7 @@ inline bool _is_symbol(CharType c) {
static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return !is_symbol(c);
}
static bool _is_whitespace(CharType c) {
@@ -104,6 +104,13 @@ static CharType _get_right_pair_symbol(CharType c) {
return 0;
}
+static int _find_first_non_whitespace_column_of_line(const String &line) {
+ int left = 0;
+ while (left < line.length() && _is_whitespace(line[left]))
+ left++;
+ return left;
+}
+
void TextEdit::Text::set_font(const Ref<Font> &p_font) {
font = p_font;
@@ -292,6 +299,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
line.marked = false;
line.safe = false;
line.breakpoint = false;
+ line.bookmark = false;
line.hidden = false;
line.width_cache = -1;
line.wrap_amount_cache = -1;
@@ -346,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
@@ -3921,7 +4078,7 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
cursor.line = p_row;
int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index);
- if (is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
+ if (n_col != 0 && is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
Vector<String> rows = get_wrap_rows_text(p_row);
int row_end_col = 0;
for (int i = 0; i < p_wrap_index + 1; i++) {
@@ -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) {
@@ -4487,6 +4682,8 @@ bool TextEdit::has_keyword_color(String p_keyword) const {
}
Color TextEdit::get_keyword_color(String p_keyword) const {
+
+ ERR_FAIL_COND_V(!keywords.has(p_keyword), Color());
return keywords[p_keyword];
}
@@ -4955,6 +5152,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 +5233,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 +5462,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 +5613,9 @@ void TextEdit::undo() {
TextOperation op = undo_stack_pos->get();
_do_text_op(op, true);
+ if (op.type != TextOperation::TYPE_INSERT && (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 +5742,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 +5750,16 @@ bool TextEdit::is_drawing_tabs() const {
return draw_tabs;
}
+void TextEdit::set_draw_spaces(bool p_draw) {
+
+ draw_spaces = p_draw;
+}
+
+bool TextEdit::is_drawing_spaces() const {
+
+ return draw_spaces;
+}
+
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
@@ -5662,19 +5939,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 +6005,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 +6013,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 +6085,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 +6167,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 +6281,15 @@ void TextEdit::set_line_length_guideline_column(int p_column) {
update();
}
+void TextEdit::set_bookmark_gutter_enabled(bool p_draw) {
+ draw_bookmark_gutter = p_draw;
+ update();
+}
+
+bool TextEdit::is_bookmark_gutter_enabled() const {
+ return draw_bookmark_gutter;
+}
+
void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
draw_breakpoint_gutter = p_draw;
update();
@@ -6020,14 +6326,32 @@ int TextEdit::get_fold_gutter_width() const {
return cache.fold_gutter_width;
}
-void TextEdit::set_hiding_enabled(int p_enabled) {
+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(bool p_enabled) {
if (!p_enabled)
unhide_all_lines();
hiding_enabled = p_enabled;
update();
}
-int TextEdit::is_hiding_enabled() const {
+bool TextEdit::is_hiding_enabled() const {
return hiding_enabled;
}
@@ -6176,8 +6500,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 +6554,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 +6578,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 +6595,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 +6614,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 +6686,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 +6711,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..0c26602d2b 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,8 +691,14 @@ public:
void set_fold_gutter_width(int p_gutter_width);
int get_fold_gutter_width() const;
- void set_hiding_enabled(int p_enabled);
- int is_hiding_enabled() 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(bool p_enabled);
+ bool is_hiding_enabled() const;
void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
@@ -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..522c1ecb6a 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"
@@ -40,9 +39,11 @@
#include "scene/main/viewport.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
#endif
+#include <limits.h>
+
void TreeItem::move_to_top() {
if (!parent || parent->children == this)
@@ -317,7 +318,7 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName
void TreeItem::set_collapsed(bool p_collapsed) {
- if (collapsed == p_collapsed)
+ if (collapsed == p_collapsed || !tree)
return;
collapsed = p_collapsed;
TreeItem *ci = tree->selected_item;
@@ -343,8 +344,7 @@ void TreeItem::set_collapsed(bool p_collapsed) {
}
_changed_notify();
- if (tree)
- tree->emit_signal("item_collapsed", this);
+ tree->emit_signal("item_collapsed", this);
}
bool TreeItem::is_collapsed() {
@@ -388,7 +388,7 @@ TreeItem *TreeItem::get_children() {
return children;
}
-TreeItem *TreeItem::get_prev_visible() {
+TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
TreeItem *current = this;
@@ -397,8 +397,20 @@ TreeItem *TreeItem::get_prev_visible() {
if (!prev) {
current = current->parent;
- if (!current || (current == tree->root && tree->hide_root))
+ if (current == tree->root && tree->hide_root) {
return NULL;
+ } else if (!current) {
+ if (p_wrap) {
+ current = this;
+ TreeItem *temp = this->get_next_visible();
+ while (temp) {
+ current = temp;
+ temp = temp->get_next_visible();
+ }
+ } else {
+ return NULL;
+ }
+ }
} else {
current = prev;
@@ -414,7 +426,7 @@ TreeItem *TreeItem::get_prev_visible() {
return current;
}
-TreeItem *TreeItem::get_next_visible() {
+TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *current = this;
@@ -432,10 +444,14 @@ TreeItem *TreeItem::get_next_visible() {
current = current->parent;
}
- if (current == NULL)
- return NULL;
- else
+ if (!current) {
+ if (p_wrap)
+ return tree->root;
+ else
+ return NULL;
+ } else {
current = current->next;
+ }
}
return current;
@@ -739,8 +755,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
- ClassDB::bind_method(D_METHOD("get_next_visible"), &TreeItem::get_next_visible);
- ClassDB::bind_method(D_METHOD("get_prev_visible"), &TreeItem::get_prev_visible);
+ ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
@@ -940,6 +956,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 +977,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 +1985,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
item_h += child_h;
}
}
+ if (p_item == root && p_button == BUTTON_RIGHT) {
+ emit_signal("empty_rmb", get_local_mouse_position());
+ }
}
return item_h; // nothing found
@@ -2018,7 +2039,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 +2284,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
next = _n;
} else {
- return;
+ break;
}
}
if (next == selected_item)
@@ -2299,7 +2322,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
prev = _n;
} else {
- return;
+ break;
}
}
if (prev == selected_item)
@@ -2845,7 +2868,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 +2915,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 +3406,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 +3506,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
+ TreeItem *from = p_at;
while (p_at) {
for (int i = 0; i < columns.size(); i++) {
@@ -3491,9 +3518,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
}
if (p_backwards)
- p_at = p_at->get_prev_visible();
+ p_at = p_at->get_prev_visible(true);
else
- p_at = p_at->get_next_visible();
+ p_at = p_at->get_next_visible(true);
+
+ if ((p_at) == from)
+ break;
}
return NULL;
@@ -3501,10 +3531,14 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) {
- if (!root)
+ TreeItem *from = get_selected();
+
+ if (!from)
+ from = root;
+ if (!from)
return NULL;
- return _search_item_text(root, p_find, r_col, p_selectable);
+ return _search_item_text(from->get_next_visible(true), p_find, r_col, p_selectable);
}
void Tree::_do_incr_search(const String &p_add) {
@@ -3513,7 +3547,7 @@ void Tree::_do_incr_search(const String &p_add) {
uint64_t diff = time - last_keypress;
if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)))
incr_search = p_add;
- else
+ else if (incr_search != p_add)
incr_search += p_add;
last_keypress = time;
@@ -3686,6 +3720,10 @@ String Tree::get_tooltip(const Point2 &p_pos) const {
const TreeItem::Cell &c = it->cells[col];
int col_width = get_column_width(col);
+
+ for (int i = 0; i < col; i++)
+ pos.x -= get_column_width(i);
+
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture> b = c.buttons[j].texture;
Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
@@ -3845,6 +3883,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("cell_selected"));
ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected")));
ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::VECTOR2, "position")));
+ ADD_SIGNAL(MethodInfo("empty_rmb", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("empty_tree_rmb_selected", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("item_edited"));
ADD_SIGNAL(MethodInfo("item_rmb_edited"));
@@ -3930,7 +3969,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..ee23b24b3c 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();
}
}
@@ -1155,7 +1165,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
ERR_FAIL_NULL(p_child);
if (p_child == this) {
- ERR_EXPLAIN("Can't add child '" + p_child->get_name() + "' to itself.")
+ ERR_EXPLAIN("Can't add child '" + p_child->get_name() + "' to itself.");
ERR_FAIL_COND(p_child == this); // adding to itself!
}
@@ -1189,7 +1199,7 @@ void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_legible_uniq
if (is_a_parent_of(p_node)) {
move_child(p_child, p_node->get_position_in_parent() + 1);
} else {
- WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent.")
+ WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent.");
}
}
@@ -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..7f0cebd492 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -105,6 +105,11 @@ void SceneTree::node_removed(Node *p_node) {
call_skip.insert(p_node);
}
+void SceneTree::node_renamed(Node *p_node) {
+
+ emit_signal(node_renamed_name, p_node);
+}
+
SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) {
Map<StringName, Group>::Element *E = group_map.find(p_group);
@@ -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;
@@ -1238,7 +1244,7 @@ void SceneTree::_update_root_rect() {
root->update_canvas_items(); //force them to update just in case
if (use_font_oversampling) {
- WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'.")
+ WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'.");
}
} break;
@@ -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 a5d86c3e1a..c02873cdeb 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -391,9 +391,11 @@ 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());
@@ -659,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();
@@ -880,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;
@@ -889,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);
}
@@ -908,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;
}
}
@@ -1007,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;
}
}
@@ -1044,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;
}
}
@@ -1064,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());
}
@@ -2476,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) {
@@ -2561,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);
@@ -2695,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
@@ -2708,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());
}
@@ -2738,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;
@@ -3003,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);
@@ -3057,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");
@@ -3139,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());
@@ -3193,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 831c285517..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;
@@ -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..0423fcb5f0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -48,6 +48,7 @@
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/line_2d.h"
#include "scene/2d/mesh_instance_2d.h"
+#include "scene/2d/multimesh_instance_2d.h"
#include "scene/2d/navigation_2d.h"
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
@@ -139,11 +140,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 +209,7 @@
#include "scene/3d/visibility_notifier.h"
#include "scene/animation/skeleton_ik.h"
#include "scene/resources/environment.h"
+#include "scene/resources/mesh_library.h"
#endif
static Ref<ResourceFormatSaverText> resource_saver_text;
@@ -467,7 +469,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
#endif
- ClassDB::register_class<MeshLibrary>();
+
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
ClassDB::register_class<Shader>();
@@ -475,7 +477,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 +490,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 +517,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>();
@@ -520,6 +544,7 @@ void register_scene_types() {
ClassDB::register_class<Position2D>();
ClassDB::register_class<Line2D>();
ClassDB::register_class<MeshInstance2D>();
+ ClassDB::register_class<MultiMeshInstance2D>();
ClassDB::register_virtual_class<CollisionObject2D>();
ClassDB::register_virtual_class<PhysicsBody2D>();
ClassDB::register_class<StaticBody2D>();
@@ -564,9 +589,13 @@ void register_scene_types() {
SceneTree::add_idle_callback(ParticlesMaterial::flush_changes);
ParticlesMaterial::init_shaders();
-#ifndef _3D_DISABLED
ClassDB::register_virtual_class<Mesh>();
ClassDB::register_class<ArrayMesh>();
+ ClassDB::register_class<MultiMesh>();
+ ClassDB::register_class<SurfaceTool>();
+ ClassDB::register_class<MeshDataTool>();
+
+#ifndef _3D_DISABLED
ClassDB::register_virtual_class<PrimitiveMesh>();
ClassDB::register_class<CapsuleMesh>();
ClassDB::register_class<CubeMesh>();
@@ -580,7 +609,6 @@ void register_scene_types() {
SceneTree::add_idle_callback(SpatialMaterial::flush_changes);
SpatialMaterial::init_shaders();
- ClassDB::register_class<MultiMesh>();
ClassDB::register_class<MeshLibrary>();
OS::get_singleton()->yield(); //may take time to init
@@ -591,13 +619,11 @@ 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>();
- ClassDB::register_class<SurfaceTool>();
- ClassDB::register_class<MeshDataTool>();
-
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_class<SpatialVelocityTracker>();
@@ -614,11 +640,13 @@ 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>();
ClassDB::register_class<ProxyTexture>();
ClassDB::register_class<AnimatedTexture>();
+ ClassDB::register_class<CameraTexture>();
ClassDB::register_class<CubeMap>();
ClassDB::register_virtual_class<TextureLayered>();
ClassDB::register_class<Texture3D>();
@@ -760,7 +788,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..1ca643cd7a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -93,7 +93,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
PoolVector<float> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % 12, false); // shuld be multiple of 11
+ ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 11
PoolVector<float>::Read r = values.read();
@@ -819,15 +819,17 @@ int Animation::_insert(float p_time, T &p_keys, const V &p_value) {
while (true) {
- if (idx == 0 || p_keys[idx - 1].time < p_time) {
- //condition for insertion.
- p_keys.insert(idx, p_value);
- return idx;
- } else if (p_keys[idx - 1].time == p_time) {
+ // Condition for replacement.
+ if (idx > 0 && Math::is_equal_approx(p_keys[idx - 1].time, p_time)) {
- // condition for replacing.
p_keys.write[idx - 1] = p_value;
return idx - 1;
+
+ // Condition for insert.
+ } else if (idx == 0 || p_keys[idx - 1].time < p_time) {
+
+ p_keys.insert(idx, p_value);
+ return idx;
}
idx--;
@@ -1296,6 +1298,78 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_V(-1);
}
+void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+
+ switch (t->type) {
+
+ case TYPE_TRANSFORM: {
+
+ TransformTrack *tt = static_cast<TransformTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+ TKey<TransformKey> key = tt->transforms[p_key_idx];
+ key.time = p_time;
+ tt->transforms.remove(p_key_idx);
+ _insert(p_time, tt->transforms, key);
+ return;
+ }
+ case TYPE_VALUE: {
+
+ ValueTrack *vt = static_cast<ValueTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, vt->values.size());
+ TKey<Variant> key = vt->values[p_key_idx];
+ key.time = p_time;
+ vt->values.remove(p_key_idx);
+ _insert(p_time, vt->values, key);
+ return;
+ }
+ case TYPE_METHOD: {
+
+ MethodTrack *mt = static_cast<MethodTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, mt->methods.size());
+ MethodKey key = mt->methods[p_key_idx];
+ key.time = p_time;
+ mt->methods.remove(p_key_idx);
+ _insert(p_time, mt->methods, key);
+ return;
+ }
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, bt->values.size());
+ TKey<BezierKey> key = bt->values[p_key_idx];
+ key.time = p_time;
+ bt->values.remove(p_key_idx);
+ _insert(p_time, bt->values, key);
+ return;
+ }
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, at->values.size());
+ TKey<AudioKey> key = at->values[p_key_idx];
+ key.time = p_time;
+ at->values.remove(p_key_idx);
+ _insert(p_time, at->values, key);
+ return;
+ }
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, at->values.size());
+ TKey<StringName> key = at->values[p_key_idx];
+ key.time = p_time;
+ at->values.remove(p_key_idx);
+ _insert(p_time, at->values, key);
+ return;
+ }
+ }
+
+ ERR_FAIL();
+}
+
float Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
@@ -1351,7 +1425,9 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+
Dictionary d = p_value;
+
if (d.has("location"))
tt->transforms.write[p_key_idx].value.loc = d["location"];
if (d.has("rotation"))
@@ -1364,6 +1440,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
ValueTrack *vt = static_cast<ValueTrack *>(t);
ERR_FAIL_INDEX(p_key_idx, vt->values.size());
+
vt->values.write[p_key_idx].value = p_value;
} break;
@@ -1371,11 +1448,14 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
MethodTrack *mt = static_cast<MethodTrack *>(t);
ERR_FAIL_INDEX(p_key_idx, mt->methods.size());
+
Dictionary d = p_value;
+
if (d.has("method"))
mt->methods.write[p_key_idx].method = d["method"];
if (d.has("args"))
mt->methods.write[p_key_idx].params = d["args"];
+
} break;
case TYPE_BEZIER: {
@@ -1395,6 +1475,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
case TYPE_AUDIO: {
AudioTrack *at = static_cast<AudioTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, at->values.size());
Dictionary k = p_value;
ERR_FAIL_COND(!k.has("start_offset"));
@@ -1409,6 +1490,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
case TYPE_ANIMATION: {
AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, at->values.size());
at->values.write[p_key_idx].value = p_value;
@@ -1466,7 +1548,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 +1559,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 +1762,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 +1773,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 +1789,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 +1805,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 +2641,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 +2666,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 +2711,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 +2738,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 +2756,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 +2771,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 +2876,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 +2915,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 +2965,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/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..2c5dfc375c 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);
@@ -534,8 +541,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// File Dialog
- theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
theme->set_icon("parent_folder", "FileDialog", make_icon(icon_parent_folder_png));
+ theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
+ theme->set_icon("toggle_hidden", "FileDialog", make_icon(icon_visibility_png));
// Popup
@@ -575,14 +583,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// GraphNode
- Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 5);
+ Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 6);
+ Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 6);
+ Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 6);
+ Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 6);
Ref<StyleBoxTexture> graphsbdefault = make_stylebox(graph_node_default_png, 4, 4, 4, 4, 6, 4, 4, 4);
Ref<StyleBoxTexture> graphsbdeffocus = make_stylebox(graph_node_default_focus_png, 4, 4, 4, 4, 6, 4, 4, 4);
- Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 5);
+ Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 6);
+ Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 6);
//graphsb->set_expand_margin_size(MARGIN_LEFT,10);
//graphsb->set_expand_margin_size(MARGIN_RIGHT,10);
@@ -795,7 +803,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 +838,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/font_hidpi.inc b/scene/resources/default_theme/font_hidpi.inc
index 2fc0f56c3f..4860149e6b 100644
--- a/scene/resources/default_theme/font_hidpi.inc
+++ b/scene/resources/default_theme/font_hidpi.inc
@@ -1,3 +1,4 @@
+/* clang-format off */
static const int _hidpi_font_height=25;
static const int _hidpi_font_ascent=19;
static const int _hidpi_font_charcount=191;
@@ -25459,3 +25460,4 @@ static const unsigned char _hidpi_font_img_data[25255]={
96,
130,
};
+/* clang-format on */
diff --git a/scene/resources/default_theme/font_lodpi.inc b/scene/resources/default_theme/font_lodpi.inc
index 8eae45a8a7..959e2c1d7b 100644
--- a/scene/resources/default_theme/font_lodpi.inc
+++ b/scene/resources/default_theme/font_lodpi.inc
@@ -1,3 +1,4 @@
+/* clang-format off */
static const int _lodpi_font_height=14;
static const int _lodpi_font_ascent=11;
static const int _lodpi_font_charcount=191;
@@ -13113,3 +13114,4 @@ static const unsigned char _lodpi_font_img_data[12909]={
96,
130,
};
+/* clang-format on */
diff --git a/scene/resources/default_theme/icon_visibility.png b/scene/resources/default_theme/icon_visibility.png
new file mode 100644
index 0000000000..6402571f3e
--- /dev/null
+++ b/scene/resources/default_theme/icon_visibility.png
Binary files differ
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..5e13a6625a 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -194,6 +194,10 @@ static const unsigned char icon_stop_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, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x2, 0x7c, 0x60, 0x26, 0x28, 0xf3, 0xf0, 0x3f, 0x76, 0x8, 0x94, 0xa2, 0x97, 0x82, 0x51, 0x5, 0x84, 0x23, 0x8b, 0x30, 0x0, 0x0, 0x66, 0x60, 0x11, 0xdc, 0x92, 0xb3, 0xb7, 0xe7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char icon_visibility_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x3, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0xdb, 0xe1, 0x4f, 0xe0, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xdf, 0xdf, 0xdf, 0xe3, 0xe3, 0xe3, 0xe6, 0xe6, 0xe6, 0xd5, 0xd5, 0xd5, 0xd8, 0xd8, 0xd8, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xe1, 0xe1, 0xe1, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xde, 0xde, 0xde, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xb7, 0x7e, 0xd, 0xb6, 0x0, 0x0, 0x0, 0x32, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x8, 0x9, 0xa, 0xc, 0xd, 0xe, 0xf, 0x11, 0x12, 0x13, 0x2e, 0x2f, 0x32, 0x33, 0x36, 0x37, 0x38, 0x48, 0x49, 0x4b, 0x50, 0x53, 0x55, 0x56, 0x6c, 0x6d, 0x6e, 0x70, 0x77, 0x79, 0x7b, 0x7c, 0xc5, 0xd7, 0xd8, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1, 0xe2, 0xe3, 0xf0, 0xf2, 0xf3, 0xf4, 0xfe, 0x5e, 0x62, 0x1a, 0x26, 0x0, 0x0, 0x0, 0x86, 0x49, 0x44, 0x41, 0x54, 0x18, 0x19, 0x8d, 0xc1, 0xb, 0x16, 0x42, 0x40, 0x0, 0x86, 0xd1, 0x2f, 0xa2, 0x77, 0x2a, 0x85, 0xde, 0x91, 0x5e, 0x33, 0x8a, 0x7f, 0xff, 0x9b, 0xcb, 0x99, 0x63, 0x1, 0xee, 0xa5, 0x9f, 0xc1, 0x3e, 0x6f, 0xea, 0xfc, 0xe0, 0xd1, 0x99, 0xdd, 0xe5, 0x94, 0xb, 0x9c, 0xf9, 0x57, 0x26, 0x9, 0x82, 0x5d, 0xa1, 0xdf, 0x92, 0x96, 0xf7, 0x94, 0x99, 0xd0, 0x1a, 0x1b, 0x7d, 0x7c, 0xe0, 0x2c, 0x25, 0x6c, 0x2b, 0x1b, 0x93, 0x4a, 0x17, 0xa0, 0x94, 0x2, 0xac, 0x64, 0x8, 0xa5, 0x12, 0x78, 0x48, 0x1, 0x56, 0x32, 0x8c, 0xa4, 0x7, 0x70, 0x93, 0x76, 0xc4, 0xd6, 0x6c, 0xc8, 0xa4, 0x2b, 0x30, 0xac, 0x54, 0x8c, 0x69, 0x4d, 0xad, 0xcc, 0x90, 0xd6, 0xba, 0x91, 0x49, 0xc3, 0x30, 0xb3, 0x6a, 0x22, 0x9c, 0xd5, 0x5b, 0xce, 0x2b, 0xa2, 0xe3, 0x9f, 0xf2, 0xba, 0xce, 0x8f, 0x3e, 0xbd, 0xfc, 0x1, 0xdb, 0xf3, 0x10, 0xc5, 0x78, 0x85, 0x14, 0x89, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char icon_zoom_less_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, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x31, 0xe0, 0xc1, 0x7f, 0x3c, 0x90, 0xb0, 0x82, 0x11, 0x2, 0x0, 0xbf, 0x57, 0x36, 0x25, 0x52, 0x24, 0x7b, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -358,6 +362,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 +410,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/dynamic_font.h b/scene/resources/dynamic_font.h
index 887c7b42c8..8d60ea1798 100644
--- a/scene/resources/dynamic_font.h
+++ b/scene/resources/dynamic_font.h
@@ -303,7 +303,6 @@ VARIANT_ENUM_CAST(DynamicFont::SpacingType);
/////////////
class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderDynamicFont, ResourceFormatLoader)
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
diff --git a/scene/resources/dynamic_font_stb.h b/scene/resources/dynamic_font_stb.h
index 4c98487600..caee6e7e32 100644
--- a/scene/resources/dynamic_font_stb.h
+++ b/scene/resources/dynamic_font_stb.h
@@ -180,7 +180,6 @@ public:
/////////////
class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderDynamicFont, ResourceFormatLoader)
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 17609ed505..7c3867beaa 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -111,6 +111,11 @@ void Environment::set_ambient_light_sky_contribution(float p_energy) {
VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, ambient_energy, ambient_sky_contribution);
}
+void Environment::set_camera_feed_id(int p_camera_feed_id) {
+ camera_feed_id = p_camera_feed_id;
+ VS::get_singleton()->environment_set_camera_feed_id(environment, camera_feed_id);
+};
+
Environment::BGMode Environment::get_background() const {
return bg_mode;
@@ -165,6 +170,10 @@ float Environment::get_ambient_light_sky_contribution() const {
return ambient_sky_contribution;
}
+int Environment::get_camera_feed_id(void) const {
+
+ return camera_feed_id;
+}
void Environment::set_tonemapper(ToneMapper p_tone_mapper) {
@@ -321,6 +330,12 @@ void Environment::_validate_property(PropertyInfo &property) const {
}
}
+ if (property.name == "background_camera_feed_id") {
+ if (bg_mode != BG_CAMERA_FEED) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+
static const char *hide_prefixes[] = {
"fog_",
"auto_exposure_",
@@ -946,6 +961,7 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ambient_light_color", "color"), &Environment::set_ambient_light_color);
ClassDB::bind_method(D_METHOD("set_ambient_light_energy", "energy"), &Environment::set_ambient_light_energy);
ClassDB::bind_method(D_METHOD("set_ambient_light_sky_contribution", "energy"), &Environment::set_ambient_light_sky_contribution);
+ ClassDB::bind_method(D_METHOD("set_camera_feed_id", "camera_feed_id"), &Environment::set_camera_feed_id);
ClassDB::bind_method(D_METHOD("get_background"), &Environment::get_background);
ClassDB::bind_method(D_METHOD("get_sky"), &Environment::get_sky);
@@ -959,9 +975,10 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ambient_light_color"), &Environment::get_ambient_light_color);
ClassDB::bind_method(D_METHOD("get_ambient_light_energy"), &Environment::get_ambient_light_energy);
ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution);
+ ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &Environment::get_camera_feed_id);
ADD_GROUP("Background", "background_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Color+Sky,Canvas,Keep"), "set_background", "get_background");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Color+Sky,Canvas,Keep,Camera Feed"), "set_background", "get_background");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "background_sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_sky", "get_sky");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "background_sky_custom_fov", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_sky_custom_fov", "get_sky_custom_fov");
ADD_PROPERTY(PropertyInfo(Variant::BASIS, "background_sky_orientation"), "set_sky_orientation", "get_sky_orientation");
@@ -970,6 +987,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_bg_color", "get_bg_color");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "background_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy", "get_bg_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "background_canvas_max_layer", PROPERTY_HINT_RANGE, "-1000,1000,1"), "set_canvas_max_layer", "get_canvas_max_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "background_camera_feed_id", PROPERTY_HINT_RANGE, "1,10,1"), "set_camera_feed_id", "get_camera_feed_id");
ADD_GROUP("Ambient Light", "ambient_light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy");
@@ -1265,6 +1283,7 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(BG_SKY);
BIND_ENUM_CONSTANT(BG_COLOR_SKY);
BIND_ENUM_CONSTANT(BG_CANVAS);
+ BIND_ENUM_CONSTANT(BG_CAMERA_FEED);
BIND_ENUM_CONSTANT(BG_MAX);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_ADDITIVE);
@@ -1294,8 +1313,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) {
@@ -1310,6 +1329,7 @@ Environment::Environment() :
ambient_energy = 1.0;
//ambient_sky_contribution = 1.0;
set_ambient_light_sky_contribution(1.0);
+ set_camera_feed_id(1);
tone_mapper = TONE_MAPPER_LINEAR;
tonemap_exposure = 1.0;
@@ -1346,7 +1366,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/environment.h b/scene/resources/environment.h
index a54f13a88f..acce9c09a2 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -49,6 +49,7 @@ public:
BG_COLOR_SKY,
BG_CANVAS,
BG_KEEP,
+ BG_CAMERA_FEED,
BG_MAX
};
@@ -98,6 +99,7 @@ private:
Color ambient_color;
float ambient_energy;
float ambient_sky_contribution;
+ int camera_feed_id;
ToneMapper tone_mapper;
float tonemap_exposure;
@@ -192,6 +194,7 @@ public:
void set_ambient_light_color(const Color &p_color);
void set_ambient_light_energy(float p_energy);
void set_ambient_light_sky_contribution(float p_energy);
+ void set_camera_feed_id(int p_camera_feed_id);
BGMode get_background() const;
Ref<Sky> get_sky() const;
@@ -205,6 +208,7 @@ public:
Color get_ambient_light_color() const;
float get_ambient_light_energy() const;
float get_ambient_light_sky_contribution() const;
+ int get_camera_feed_id(void) const;
void set_tonemapper(ToneMapper p_tone_mapper);
ToneMapper get_tonemapper() const;
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..436ed43c42 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;
@@ -201,7 +202,6 @@ public:
};
class ResourceFormatLoaderBMFont : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderBMFont, ResourceFormatLoader)
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
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..6dec4a0c49 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);
@@ -2075,7 +2094,7 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "params_billboard_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particle Billboard"), "set_billboard_mode", "get_billboard_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "params_billboard_keep_scale"), "set_flag", "get_flag", FLAG_BILLBOARD_KEEP_SCALE);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "params_grow"), "set_grow_enabled", "is_grow_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_grow_amount", PROPERTY_HINT_RANGE, "-16,10,0.01"), "set_grow", "get_grow");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_grow_amount", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_grow", "get_grow");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "params_use_alpha_scissor"), "set_flag", "get_flag", FLAG_USE_ALPHA_SCISSOR);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
ADD_GROUP("Particles Anim", "particles_anim_");
@@ -2101,7 +2120,7 @@ void SpatialMaterial::_bind_methods() {
ADD_GROUP("Emission", "emission_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_enabled"), "set_feature", "get_feature", FEATURE_EMISSION);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_emission_energy", "get_emission_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
@@ -2183,11 +2202,11 @@ void SpatialMaterial::_bind_methods() {
ADD_GROUP("Proximity Fade", "proximity_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.1"), "set_proximity_fade_distance", "get_proximity_fade_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance");
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "distance_fade_mode", PROPERTY_HINT_ENUM, "Disabled,PixelAlpha,PixelDither,ObjectDither"), "set_distance_fade", "get_distance_fade");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.1"), "set_distance_fade_min_distance", "get_distance_fade_min_distance");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.1"), "set_distance_fade_max_distance", "get_distance_fade_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_min_distance", "get_distance_fade_min_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_max_distance", "get_distance_fade_max_distance");
BIND_ENUM_CONSTANT(TEXTURE_ALBEDO);
BIND_ENUM_CONSTANT(TEXTURE_METALLIC);
@@ -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/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..99286668ce 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -92,8 +92,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
if (i > 0) {
- ERR_EXPLAIN(vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name]))
- ERR_FAIL_COND_V(n.parent == -1, NULL)
+ ERR_EXPLAIN(vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name]));
+ ERR_FAIL_COND_V(n.parent == -1, NULL);
NODE_FROM_ID(nparent, n.parent);
#ifdef DEBUG_ENABLED
if (!nparent && (n.parent & FLAG_ID_IS_PATH)) {
@@ -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 b6efca9acc..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;
- sorted_er.write[E->get()] = E->key();
+ for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
+
+ 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())
@@ -1664,15 +1712,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
Vector<StringName> groups = state->get_node_groups(i);
String header = "[node";
- header += " name=\"" + String(name) + "\"";
+ header += " name=\"" + String(name).c_escape() + "\"";
if (type != StringName()) {
header += " type=\"" + String(type) + "\"";
}
if (path != NodePath()) {
- header += " parent=\"" + String(path.simplified()) + "\"";
+ header += " parent=\"" + String(path.simplified()).c_escape() + "\"";
}
if (owner != NodePath() && owner != NodePath(".")) {
- header += " owner=\"" + String(owner.simplified()) + "\"";
+ header += " owner=\"" + String(owner.simplified()).c_escape() + "\"";
}
if (index >= 0) {
header += " index=\"" + itos(index) + "\"";
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 8d78ab33b0..0041989f09 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -128,7 +128,6 @@ public:
};
class ResourceFormatLoaderText : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderText, ResourceFormatLoader)
public:
static ResourceFormatLoaderText *singleton;
virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
@@ -169,6 +168,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);
@@ -179,7 +186,6 @@ public:
};
class ResourceFormatSaverText : public ResourceFormatSaver {
- GDCLASS(ResourceFormatSaverText, ResourceFormatSaver)
public:
static ResourceFormatSaverText *singleton;
virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index fb493046fc..6e7447839f 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -100,7 +100,6 @@ public:
VARIANT_ENUM_CAST(Shader::Mode);
class ResourceFormatLoaderShader : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderShader, ResourceFormatLoader)
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
@@ -109,7 +108,6 @@ public:
};
class ResourceFormatSaverShader : public ResourceFormatSaver {
- GDCLASS(ResourceFormatSaverShader, ResourceFormatSaver)
public:
virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
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..6e0bc43296 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -34,7 +34,9 @@
#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"
+#include "servers/camera/camera_feed.h"
Size2 Texture::get_size() const {
@@ -110,10 +112,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) {
@@ -229,7 +233,7 @@ Image::Format ImageTexture::get_format() const {
#ifndef DISABLE_DEPRECATED
Error ImageTexture::load(const String &p_path) {
- WARN_DEPRECATED
+ WARN_DEPRECATED;
Ref<Image> img;
img.instance();
Error err = img->load(p_path);
@@ -637,7 +641,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 +663,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 +672,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 +1134,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 {
@@ -2366,3 +2499,107 @@ String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_pat
return "TextureArray";
return "";
}
+
+void CameraTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_camera_feed_id", "feed_id"), &CameraTexture::set_camera_feed_id);
+ ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &CameraTexture::get_camera_feed_id);
+
+ ClassDB::bind_method(D_METHOD("set_which_feed", "which_feed"), &CameraTexture::set_which_feed);
+ ClassDB::bind_method(D_METHOD("get_which_feed"), &CameraTexture::get_which_feed);
+
+ ClassDB::bind_method(D_METHOD("set_camera_active", "active"), &CameraTexture::set_camera_active);
+ ClassDB::bind_method(D_METHOD("get_camera_active"), &CameraTexture::get_camera_active);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "camera_feed_id"), "set_camera_feed_id", "get_camera_feed_id");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "which_feed"), "set_which_feed", "get_which_feed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
+}
+
+int CameraTexture::get_width() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_base_width();
+ } else {
+ return 0;
+ }
+}
+
+int CameraTexture::get_height() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_base_height();
+ } else {
+ return 0;
+ }
+}
+
+bool CameraTexture::has_alpha() const {
+ return false;
+}
+
+RID CameraTexture::get_rid() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_texture(which_feed);
+ } else {
+ return RID();
+ }
+}
+
+void CameraTexture::set_flags(uint32_t p_flags) {
+ // not supported
+}
+
+uint32_t CameraTexture::get_flags() const {
+ // not supported
+ return 0;
+}
+
+Ref<Image> CameraTexture::get_data() const {
+ // not (yet) supported
+ return Ref<Image>();
+}
+
+void CameraTexture::set_camera_feed_id(int p_new_id) {
+ camera_feed_id = p_new_id;
+ _change_notify();
+}
+
+int CameraTexture::get_camera_feed_id() const {
+ return camera_feed_id;
+}
+
+void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
+ which_feed = p_which;
+ _change_notify();
+}
+
+CameraServer::FeedImage CameraTexture::get_which_feed() const {
+ return which_feed;
+}
+
+void CameraTexture::set_camera_active(bool p_active) {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ feed->set_active(p_active);
+ _change_notify();
+ }
+}
+
+bool CameraTexture::get_camera_active() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->is_active();
+ } else {
+ return false;
+ }
+}
+
+CameraTexture::CameraTexture() {
+ camera_feed_id = 0;
+ which_feed = CameraServer::FEED_RGBA_IMAGE;
+}
+
+CameraTexture::~CameraTexture() {
+ // nothing to do here yet
+}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 281a33929c..59d243484d 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -39,6 +39,7 @@
#include "core/resource.h"
#include "scene/resources/curve.h"
#include "scene/resources/gradient.h"
+#include "servers/camera_server.h"
#include "servers/visual_server.h"
/**
@@ -238,7 +239,6 @@ public:
};
class ResourceFormatLoaderStreamTexture : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderStreamTexture, ResourceFormatLoader)
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
@@ -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);
@@ -493,7 +536,6 @@ public:
};
class ResourceFormatLoaderTextureLayered : public ResourceFormatLoader {
- GDCLASS(ResourceFormatLoaderTextureLayered, ResourceFormatLoader)
public:
enum Compression {
COMPRESSION_LOSSLESS,
@@ -697,4 +739,38 @@ public:
~AnimatedTexture();
};
+class CameraTexture : public Texture {
+ GDCLASS(CameraTexture, Texture)
+
+private:
+ int camera_feed_id;
+ CameraServer::FeedImage which_feed;
+
+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;
+
+ virtual Ref<Image> get_data() const;
+
+ void set_camera_feed_id(int p_new_id);
+ int get_camera_feed_id() const;
+
+ void set_which_feed(CameraServer::FeedImage p_which);
+ CameraServer::FeedImage get_which_feed() const;
+
+ void set_camera_active(bool p_active);
+ bool get_camera_active() const;
+
+ CameraTexture();
+ ~CameraTexture();
+};
+
#endif
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..dd595d9ff8 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "visual_shader.h"
+
#include "core/vmap.h"
#include "servers/visual/shader_types.h"
@@ -159,6 +160,7 @@ Vector2 VisualShader::get_node_position(Type p_type, int p_id) const {
ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2());
return g->nodes[p_id].position;
}
+
Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const {
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>());
const Graph *g = &graph[p_type];
@@ -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,9 +295,9 @@ 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");
- ERR_FAIL_V(ERR_INVALID_PARAMETER)
+ 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