summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp41
-rw-r--r--scene/2d/animated_sprite.h4
-rw-r--r--scene/2d/audio_stream_player_2d.cpp68
-rw-r--r--scene/2d/audio_stream_player_2d.h6
-rw-r--r--scene/2d/back_buffer_copy.cpp5
-rw-r--r--scene/2d/back_buffer_copy.h1
-rw-r--r--scene/2d/canvas_item.cpp21
-rw-r--r--scene/2d/canvas_item.h39
-rw-r--r--scene/2d/collision_object_2d.cpp25
-rw-r--r--scene/2d/collision_object_2d.h4
-rw-r--r--scene/2d/joints_2d.cpp4
-rw-r--r--scene/2d/light_2d.cpp12
-rw-r--r--scene/2d/light_2d.h2
-rw-r--r--scene/2d/line_2d.cpp3
-rw-r--r--scene/2d/line_2d.h4
-rw-r--r--scene/2d/line_builder.cpp15
-rw-r--r--scene/2d/node_2d.cpp1
-rw-r--r--scene/2d/physics_body_2d.cpp392
-rw-r--r--scene/2d/physics_body_2d.h41
-rw-r--r--scene/2d/polygon_2d.cpp2
-rw-r--r--scene/2d/remote_transform_2d.cpp2
-rw-r--r--scene/2d/screen_button.cpp14
-rw-r--r--scene/2d/screen_button.h3
-rw-r--r--scene/2d/sprite.cpp11
-rw-r--r--scene/2d/sprite.h1
-rw-r--r--scene/2d/tile_map.cpp26
-rw-r--r--scene/2d/tile_map.h7
-rw-r--r--scene/3d/audio_stream_player_3d.cpp68
-rw-r--r--scene/3d/audio_stream_player_3d.h6
-rw-r--r--scene/3d/baked_lightmap.cpp1
-rw-r--r--scene/3d/camera.cpp1
-rw-r--r--scene/3d/collision_object.h1
-rw-r--r--scene/3d/collision_shape.h1
-rw-r--r--scene/3d/cpu_particles.cpp1417
-rw-r--r--scene/3d/cpu_particles.h258
-rw-r--r--scene/3d/gi_probe.cpp1
-rw-r--r--scene/3d/light.cpp1
-rw-r--r--scene/3d/mesh_instance.cpp3
-rw-r--r--scene/3d/mesh_instance.h1
-rw-r--r--scene/3d/physics_body.cpp186
-rw-r--r--scene/3d/physics_body.h27
-rw-r--r--scene/3d/physics_joint.cpp8
-rw-r--r--scene/3d/reflection_probe.cpp1
-rw-r--r--scene/3d/remote_transform.cpp2
-rw-r--r--scene/3d/skeleton.cpp8
-rw-r--r--scene/3d/skeleton.h9
-rw-r--r--scene/3d/soft_body.cpp740
-rw-r--r--scene/3d/soft_body.h197
-rw-r--r--scene/3d/spatial.cpp26
-rw-r--r--scene/3d/spatial.h4
-rw-r--r--scene/3d/vehicle_body.cpp2
-rw-r--r--scene/3d/voxel_light_baker.cpp4
-rw-r--r--scene/animation/animation_blend_space_1d.cpp9
-rw-r--r--scene/animation/animation_blend_space_1d.h2
-rw-r--r--scene/animation/animation_blend_space_2d.cpp8
-rw-r--r--scene/animation/animation_blend_space_2d.h2
-rw-r--r--scene/animation/animation_blend_tree.cpp90
-rw-r--r--scene/animation/animation_blend_tree.h30
-rw-r--r--scene/animation/animation_node_state_machine.cpp6
-rw-r--r--scene/animation/animation_player.cpp41
-rw-r--r--scene/animation/animation_tree.cpp178
-rw-r--r--scene/animation/animation_tree.h18
-rw-r--r--scene/animation/animation_tree_player.cpp2
-rw-r--r--scene/animation/root_motion_view.cpp178
-rw-r--r--scene/animation/root_motion_view.h47
-rw-r--r--scene/audio/audio_player.cpp45
-rw-r--r--scene/audio/audio_player.h5
-rw-r--r--scene/gui/color_picker.cpp30
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/control.cpp229
-rw-r--r--scene/gui/control.h23
-rw-r--r--scene/gui/item_list.cpp3
-rw-r--r--scene/gui/label.cpp2
-rw-r--r--scene/gui/line_edit.cpp3
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp61
-rw-r--r--scene/gui/popup_menu.h5
-rw-r--r--scene/gui/rich_text_label.cpp21
-rw-r--r--scene/gui/rich_text_label.h5
-rw-r--r--scene/gui/scroll_bar.cpp6
-rw-r--r--scene/gui/slider.cpp25
-rw-r--r--scene/gui/slider.h4
-rw-r--r--scene/gui/tab_container.cpp4
-rw-r--r--scene/gui/tab_container.h6
-rw-r--r--scene/gui/text_edit.cpp168
-rw-r--r--scene/gui/text_edit.h12
-rw-r--r--scene/main/http_request.cpp20
-rw-r--r--scene/main/node.cpp30
-rw-r--r--scene/main/node.h9
-rw-r--r--scene/main/scene_tree.cpp31
-rw-r--r--scene/main/scene_tree.h3
-rw-r--r--scene/main/viewport.cpp68
-rw-r--r--scene/main/viewport.h4
-rw-r--r--scene/register_scene_types.cpp71
-rw-r--r--scene/resources/animation.cpp13
-rw-r--r--scene/resources/curve.cpp10
-rw-r--r--scene/resources/curve.h2
-rw-r--r--scene/resources/cylinder_shape.cpp116
-rw-r--r--scene/resources/cylinder_shape.h57
-rw-r--r--scene/resources/default_theme/arrow_down.pngbin184 -> 109 bytes
-rw-r--r--scene/resources/default_theme/arrow_right.pngbin183 -> 103 bytes
-rw-r--r--scene/resources/default_theme/background.pngbin1235 -> 854 bytes
-rw-r--r--scene/resources/default_theme/base_green.pngbin335 -> 86 bytes
-rw-r--r--scene/resources/default_theme/button_disabled.pngbin486 -> 256 bytes
-rw-r--r--scene/resources/default_theme/button_focus.pngbin418 -> 203 bytes
-rw-r--r--scene/resources/default_theme/button_hover.pngbin606 -> 344 bytes
-rw-r--r--scene/resources/default_theme/button_normal.pngbin598 -> 338 bytes
-rw-r--r--scene/resources/default_theme/checked.pngbin627 -> 363 bytes
-rw-r--r--scene/resources/default_theme/checker_bg.pngbin295 -> 77 bytes
-rw-r--r--scene/resources/default_theme/close.pngbin230 -> 155 bytes
-rw-r--r--scene/resources/default_theme/close_hl.pngbin230 -> 155 bytes
-rw-r--r--scene/resources/default_theme/color_picker_sample.pngbin194 -> 117 bytes
-rw-r--r--scene/resources/default_theme/default_theme.cpp5
-rw-r--r--scene/resources/default_theme/dosfont.pngbin963 -> 683 bytes
-rw-r--r--scene/resources/default_theme/dropdown.pngbin369 -> 133 bytes
-rw-r--r--scene/resources/default_theme/error_icon.pngbin362 -> 111 bytes
-rw-r--r--scene/resources/default_theme/focus.pngbin411 -> 200 bytes
-rw-r--r--scene/resources/default_theme/frame_focus.pngbin448 -> 200 bytes
-rw-r--r--scene/resources/default_theme/full_panel_bg.pngbin430 -> 207 bytes
-rw-r--r--scene/resources/default_theme/graph_node_breakpoint.pngbin277 -> 140 bytes
-rw-r--r--scene/resources/default_theme/graph_node_close.pngbin222 -> 152 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment.pngbin506 -> 382 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment_focus.pngbin482 -> 373 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default.pngbin420 -> 205 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default_focus.pngbin452 -> 195 bytes
-rw-r--r--scene/resources/default_theme/graph_node_position.pngbin278 -> 140 bytes
-rw-r--r--scene/resources/default_theme/graph_node_selected.pngbin933 -> 836 bytes
-rw-r--r--scene/resources/default_theme/graph_port.pngbin273 -> 171 bytes
-rw-r--r--scene/resources/default_theme/hseparator.pngbin323 -> 112 bytes
-rw-r--r--scene/resources/default_theme/hslider_bg.pngbin526 -> 263 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber.pngbin591 -> 300 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_disabled.pngbin386 -> 288 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_hl.pngbin734 -> 450 bytes
-rw-r--r--scene/resources/default_theme/hslider_tick.pngbin364 -> 149 bytes
-rw-r--r--scene/resources/default_theme/hsplit_bg.pngbin334 -> 85 bytes
-rw-r--r--scene/resources/default_theme/hsplitter.pngbin359 -> 97 bytes
-rw-r--r--scene/resources/default_theme/icon_add.pngbin129 -> 86 bytes
-rw-r--r--scene/resources/default_theme/icon_close.pngbin230 -> 155 bytes
-rw-r--r--scene/resources/default_theme/icon_color_pick.pngbin416 -> 227 bytes
-rw-r--r--scene/resources/default_theme/icon_folder.pngbin170 -> 103 bytes
-rw-r--r--scene/resources/default_theme/icon_parent_folder.pngbin329 -> 161 bytes
-rw-r--r--scene/resources/default_theme/icon_play.pngbin237 -> 122 bytes
-rw-r--r--scene/resources/default_theme/icon_reload.pngbin420 -> 234 bytes
-rw-r--r--scene/resources/default_theme/icon_snap_grid.pngbin325 -> 226 bytes
-rw-r--r--scene/resources/default_theme/icon_stop.pngbin312 -> 87 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_less.pngbin112 -> 76 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_more.pngbin129 -> 85 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_reset.pngbin186 -> 108 bytes
-rw-r--r--scene/resources/default_theme/line_edit.pngbin424 -> 176 bytes
-rw-r--r--scene/resources/default_theme/line_edit_disabled.pngbin406 -> 135 bytes
-rw-r--r--scene/resources/default_theme/line_edit_focus.pngbin660 -> 365 bytes
-rw-r--r--scene/resources/default_theme/logo.pngbin8809 -> 6676 bytes
-rwxr-xr-xscene/resources/default_theme/make_header.py26
-rw-r--r--scene/resources/default_theme/mini_checkerboard.pngbin166 -> 80 bytes
-rw-r--r--scene/resources/default_theme/option_arrow.pngbin227 -> 119 bytes
-rw-r--r--scene/resources/default_theme/option_button_disabled.pngbin901 -> 656 bytes
-rw-r--r--scene/resources/default_theme/option_button_focus.pngbin679 -> 416 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover.pngbin924 -> 667 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal.pngbin922 -> 669 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed.pngbin931 -> 677 bytes
-rw-r--r--scene/resources/default_theme/panel_bg.pngbin334 -> 85 bytes
-rw-r--r--scene/resources/default_theme/popup_bg.pngbin672 -> 410 bytes
-rw-r--r--scene/resources/default_theme/popup_bg_disabled.pngbin582 -> 362 bytes
-rw-r--r--scene/resources/default_theme/popup_checked.pngbin238 -> 143 bytes
-rw-r--r--scene/resources/default_theme/popup_hover.pngbin404 -> 145 bytes
-rw-r--r--scene/resources/default_theme/popup_unchecked.pngbin310 -> 98 bytes
-rw-r--r--scene/resources/default_theme/popup_window.pngbin1234 -> 903 bytes
-rw-r--r--scene/resources/default_theme/progress_bar.pngbin460 -> 194 bytes
-rw-r--r--scene/resources/default_theme/progress_fill.pngbin402 -> 112 bytes
-rw-r--r--scene/resources/default_theme/radio_checked.pngbin477 -> 257 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked.pngbin422 -> 207 bytes
-rw-r--r--scene/resources/default_theme/reference_border.pngbin348 -> 132 bytes
-rw-r--r--scene/resources/default_theme/scroll_bg.pngbin510 -> 252 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_down.pngbin418 -> 170 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_down_hl.pngbin418 -> 170 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left.pngbin444 -> 196 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left_hl.pngbin461 -> 200 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right.pngbin446 -> 198 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right_hl.pngbin464 -> 210 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_up.pngbin421 -> 173 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_up_hl.pngbin421 -> 173 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber.pngbin523 -> 264 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_hl.pngbin536 -> 278 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_pressed.pngbin233 -> 104 bytes
-rw-r--r--scene/resources/default_theme/selection.pngbin406 -> 195 bytes
-rw-r--r--scene/resources/default_theme/selection_oof.pngbin411 -> 196 bytes
-rw-r--r--scene/resources/default_theme/spinbox_updown.pngbin280 -> 146 bytes
-rw-r--r--scene/resources/default_theme/submenu.pngbin175 -> 103 bytes
-rw-r--r--scene/resources/default_theme/tab.pngbin300 -> 82 bytes
-rw-r--r--scene/resources/default_theme/tab_behind.pngbin553 -> 282 bytes
-rw-r--r--scene/resources/default_theme/tab_close.pngbin325 -> 158 bytes
-rw-r--r--scene/resources/default_theme/tab_container_bg.pngbin598 -> 338 bytes
-rw-r--r--scene/resources/default_theme/tab_current.pngbin627 -> 347 bytes
-rw-r--r--scene/resources/default_theme/tab_menu.pngbin186 -> 111 bytes
-rw-r--r--scene/resources/default_theme/tab_menu_hl.pngbin186 -> 111 bytes
-rw-r--r--scene/resources/default_theme/toggle_off.pngbin1355 -> 1077 bytes
-rw-r--r--scene/resources/default_theme/toggle_on.pngbin1318 -> 1034 bytes
-rw-r--r--scene/resources/default_theme/tool_button_pressed.pngbin1230 -> 864 bytes
-rw-r--r--scene/resources/default_theme/tooltip_bg.pngbin424 -> 198 bytes
-rw-r--r--scene/resources/default_theme/tree_bg.pngbin424 -> 176 bytes
-rw-r--r--scene/resources/default_theme/tree_bg_disabled.pngbin406 -> 135 bytes
-rw-r--r--scene/resources/default_theme/tree_bg_focus.pngbin1039 -> 696 bytes
-rw-r--r--scene/resources/default_theme/tree_cursor.pngbin743 -> 434 bytes
-rw-r--r--scene/resources/default_theme/tree_cursor_unfocus.pngbin655 -> 369 bytes
-rw-r--r--scene/resources/default_theme/tree_title.pngbin335 -> 86 bytes
-rw-r--r--scene/resources/default_theme/tree_title_pressed.pngbin335 -> 86 bytes
-rw-r--r--scene/resources/default_theme/unchecked.pngbin477 -> 222 bytes
-rw-r--r--scene/resources/default_theme/updown.pngbin241 -> 144 bytes
-rw-r--r--scene/resources/default_theme/vseparator.pngbin323 -> 108 bytes
-rw-r--r--scene/resources/default_theme/vslider_bg.pngbin532 -> 276 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber.pngbin394 -> 245 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_disabled.pngbin335 -> 237 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_hl.pngbin439 -> 261 bytes
-rw-r--r--scene/resources/default_theme/vslider_tick.pngbin360 -> 145 bytes
-rw-r--r--scene/resources/default_theme/vsplit_bg.pngbin334 -> 85 bytes
-rw-r--r--scene/resources/default_theme/vsplitter.pngbin351 -> 95 bytes
-rw-r--r--scene/resources/default_theme/window_resizer.pngbin181 -> 87 bytes
-rw-r--r--scene/resources/dynamic_font.cpp6
-rw-r--r--scene/resources/environment.cpp37
-rw-r--r--scene/resources/environment.h4
-rw-r--r--scene/resources/material.cpp23
-rw-r--r--scene/resources/material.h6
-rw-r--r--scene/resources/mesh.cpp53
-rw-r--r--scene/resources/mesh.h6
-rw-r--r--scene/resources/physics_material.cpp94
-rw-r--r--scene/resources/physics_material.h70
-rw-r--r--scene/resources/scene_format_text.cpp2
-rw-r--r--scene/resources/shader.cpp18
-rw-r--r--scene/resources/shader.h3
-rw-r--r--scene/resources/style_box.cpp36
-rw-r--r--scene/resources/style_box.h10
-rw-r--r--scene/resources/surface_tool.cpp1
-rw-r--r--scene/resources/texture.cpp205
-rw-r--r--scene/resources/texture.h69
-rw-r--r--scene/resources/theme.h12
-rw-r--r--scene/resources/tile_set.cpp2
-rw-r--r--scene/resources/visual_shader.cpp1555
-rw-r--r--scene/resources/visual_shader.h284
-rw-r--r--scene/resources/visual_shader_nodes.cpp1896
-rw-r--r--scene/resources/visual_shader_nodes.h861
240 files changed, 10072 insertions, 649 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 54194ff543..b56eedabc7 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -59,15 +59,36 @@ bool AnimatedSprite::_edit_use_pivot() const {
}
Rect2 AnimatedSprite::_edit_get_rect() const {
+ return _get_rect();
+}
+
+bool AnimatedSprite::_edit_use_rect() const {
if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
- return Node2D::_edit_get_rect();
+ return false;
+ }
+ Ref<Texture> t;
+ if (animation)
+ t = frames->get_frame(animation, frame);
+ if (t.is_null())
+ return false;
+
+ return true;
+}
+
+Rect2 AnimatedSprite::get_anchorable_rect() const {
+ return _get_rect();
+}
+
+Rect2 AnimatedSprite::_get_rect() const {
+ if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
+ return Rect2();
}
Ref<Texture> t;
if (animation)
t = frames->get_frame(animation, frame);
if (t.is_null())
- return Node2D::_edit_get_rect();
+ return Rect2();
Size2 s = t->get_size();
Point2 ofs = offset;
@@ -80,10 +101,6 @@ Rect2 AnimatedSprite::_edit_get_rect() const {
return Rect2(ofs, s);
}
-bool AnimatedSprite::_edit_use_rect() const {
- return true;
-}
-
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
@@ -175,6 +192,16 @@ void SpriteFrames::get_animation_list(List<StringName> *r_animations) const {
}
}
+Vector<String> SpriteFrames::get_animation_names() const {
+
+ Vector<String> names;
+ for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ names.push_back(E->key());
+ }
+ names.sort();
+ return names;
+}
+
void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) {
ERR_FAIL_COND(p_fps < 0);
@@ -266,6 +293,8 @@ void SpriteFrames::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation);
ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation);
+ ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names);
+
ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed);
ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed);
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index be5b1ef6d6..f6586aff36 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -72,6 +72,7 @@ public:
void rename_animation(const StringName &p_prev, const StringName &p_next);
void get_animation_list(List<StringName> *r_animations) const;
+ Vector<String> get_animation_names() const;
void set_animation_speed(const StringName &p_anim, float p_fps);
float get_animation_speed(const StringName &p_anim) const;
@@ -145,6 +146,7 @@ class AnimatedSprite : public Node2D {
void _reset_timeout();
void _set_playing(bool p_playing);
bool _is_playing() const;
+ Rect2 _get_rect() const;
protected:
static void _bind_methods();
@@ -161,6 +163,8 @@ public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
+
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 54541293fd..507499a324 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -36,11 +36,8 @@
void AudioStreamPlayer2D::_mix_audio() {
- if (!stream_playback.is_valid()) {
- return;
- }
-
- if (!active) {
+ if (!stream_playback.is_valid() || !active ||
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -53,7 +50,11 @@ void AudioStreamPlayer2D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- //mix
+ if (stream_paused_fade_out) {
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
+ }
+
stream_playback->mix(buffer, pitch_scale, buffer_size);
//write all outputs
@@ -83,8 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() {
}
//mix!
- AudioFrame vol_inc = (current.vol - prev_outputs[i].vol) / float(buffer_size);
- AudioFrame vol = current.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_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
int cc = AudioServer::get_singleton()->get_channel_count();
@@ -125,6 +128,8 @@ void AudioStreamPlayer2D::_mix_audio() {
}
output_ready = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
void AudioStreamPlayer2D::_notification(int p_what) {
@@ -142,6 +147,17 @@ void AudioStreamPlayer2D::_notification(int p_what) {
AudioServer::get_singleton()->remove_callback(_mix_audios, this);
}
+ if (p_what == NOTIFICATION_PAUSED) {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ }
+
+ if (p_what == NOTIFICATION_UNPAUSED) {
+ set_stream_paused(false);
+ }
+
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
@@ -242,7 +258,6 @@ void AudioStreamPlayer2D::_notification(int p_what) {
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
- ERR_FAIL_COND(!p_stream.is_valid());
AudioServer::get_singleton()->lock();
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
@@ -254,14 +269,15 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
setseek = -1;
}
- stream = p_stream;
- stream_playback = p_stream->instance_playback();
+ if (p_stream.is_valid()) {
+ stream = p_stream;
+ stream_playback = p_stream->instance_playback();
+ }
AudioServer::get_singleton()->unlock();
- if (stream_playback.is_null()) {
+ if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
- ERR_FAIL_COND(stream_playback.is_null());
}
}
@@ -288,6 +304,11 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
void AudioStreamPlayer2D::play(float p_from_pos) {
+ if (!is_playing()) {
+ // Reset the prev_output_count if the stream is stopped
+ prev_output_count = 0;
+ }
+
if (stream_playback.is_valid()) {
setplay = p_from_pos;
output_ready = false;
@@ -418,6 +439,20 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
return area_mask;
}
+void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
+
+ if (p_pause != stream_paused) {
+ stream_paused = p_pause;
+ stream_paused_fade_in = p_pause ? false : true;
+ stream_paused_fade_out = p_pause ? true : false;
+ }
+}
+
+bool AudioStreamPlayer2D::get_stream_paused() const {
+
+ return stream_paused;
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
@@ -454,6 +489,9 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_area_mask", "mask"), &AudioStreamPlayer2D::set_area_mask);
ClassDB::bind_method(D_METHOD("get_area_mask"), &AudioStreamPlayer2D::get_area_mask);
+ 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("_bus_layout_changed"), &AudioStreamPlayer2D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -461,6 +499,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "1,4096,1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
@@ -483,6 +522,9 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() {
setplay = -1;
output_ready = false;
area_mask = 1;
+ stream_paused = 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 9ae8e3a518..e68e6eeca5 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -72,6 +72,9 @@ private:
float volume_db;
float pitch_scale;
bool autoplay;
+ bool stream_paused;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -123,6 +126,9 @@ public:
void set_area_mask(uint32_t p_mask);
uint32_t get_area_mask() const;
+ void set_stream_paused(bool p_pause);
+ bool get_stream_paused() const;
+
AudioStreamPlayer2D();
~AudioStreamPlayer2D();
};
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index caa1adebdb..e06c30ec6b 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -59,6 +59,11 @@ bool BackBufferCopy::_edit_use_rect() const {
return true;
}
+Rect2 BackBufferCopy::get_anchorable_rect() const {
+
+ return rect;
+}
+
void BackBufferCopy::set_rect(const Rect2 &p_rect) {
rect = p_rect;
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index 752d56de2b..b1ee12544b 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -58,6 +58,7 @@ public:
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
+ Rect2 get_anchorable_rect() const;
void set_copy_mode(CopyMode p_mode);
CopyMode get_copy_mode() const;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index f1c09594da..47326b9be2 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -321,11 +321,6 @@ void CanvasItem::hide() {
_change_notify("visible");
}
-Size2 CanvasItem::_edit_get_minimum_size() const {
-
- return Size2(-1, -1); //no limit
-}
-
void CanvasItem::_update_callback() {
if (!is_inside_tree()) {
@@ -994,7 +989,6 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect);
ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect);
ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect);
- ClassDB::bind_method(D_METHOD("_edit_get_item_and_children_rect"), &CanvasItem::_edit_get_item_and_children_rect);
ClassDB::bind_method(D_METHOD("_edit_set_rotation", "degrees"), &CanvasItem::_edit_set_rotation);
ClassDB::bind_method(D_METHOD("_edit_get_rotation"), &CanvasItem::_edit_get_rotation);
ClassDB::bind_method(D_METHOD("_edit_use_rotation"), &CanvasItem::_edit_use_rotation);
@@ -1175,21 +1169,6 @@ int CanvasItem::get_canvas_layer() const {
return 0;
}
-Rect2 CanvasItem::_edit_get_item_and_children_rect() const {
-
- Rect2 rect = _edit_get_rect();
-
- for (int i = 0; i < get_child_count(); i++) {
- CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
- if (c) {
- Rect2 sir = c->get_transform().xform(c->_edit_get_item_and_children_rect());
- rect = rect.merge(sir);
- }
- }
-
- return rect;
-}
-
CanvasItem::CanvasItem() :
xform_change(this) {
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 10d5082dfc..1e6a251c9c 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -222,6 +222,9 @@ public:
/* EDITOR */
+ // Select the node
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
// Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state){};
virtual Dictionary _edit_get_state() const { return Dictionary(); };
@@ -234,36 +237,21 @@ public:
virtual void _edit_set_scale(const Size2 &p_scale) = 0;
virtual Size2 _edit_get_scale() const = 0;
+ // Used to rotate the node
+ virtual bool _edit_use_rotation() const { return false; };
+ virtual void _edit_set_rotation(float p_rotation){};
+ virtual float _edit_get_rotation() const { return 0.0; };
+
// Used to resize/move the node
+ virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode()
virtual void _edit_set_rect(const Rect2 &p_rect){};
virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); };
- virtual bool _edit_use_rect() const { return false; };
-
- Rect2 _edit_get_item_and_children_rect() const;
-
- // used to select the node
- virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
-
- // Used to rotate the node
- virtual void
- _edit_set_rotation(float p_rotation){};
- virtual float _edit_get_rotation() const {
- return 0.0;
- };
- virtual bool _edit_use_rotation() const {
- return false;
- };
+ virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD
// Used to set a pivot
+ virtual bool _edit_use_pivot() const { return false; };
virtual void _edit_set_pivot(const Point2 &p_pivot){};
- virtual Point2 _edit_get_pivot() const {
- return Point2();
- };
- virtual bool _edit_use_pivot() const {
- return false;
- };
-
- virtual Size2 _edit_get_minimum_size() const;
+ virtual Point2 _edit_get_pivot() const { return Point2(); };
/* VISIBILITY */
@@ -358,6 +346,9 @@ public:
void set_notify_transform(bool p_enable);
bool is_transform_notification_enabled() const;
+ // Used by control nodes to retreive the parent's anchorable area
+ virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); };
+
int get_canvas_layer() const;
CanvasItem();
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index d05c818ae1..cabd7fddc2 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -38,10 +38,14 @@ void CollisionObject2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
+ Transform2D global_transform = get_global_transform();
+
if (area)
- Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
+ Physics2DServer::get_singleton()->area_set_transform(rid, global_transform);
else
- Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
+ Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform);
+
+ last_transform = global_transform;
RID space = get_world_2d()->get_space();
if (area) {
@@ -60,10 +64,18 @@ void CollisionObject2D::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
+ Transform2D global_transform = get_global_transform();
+
+ if (only_update_transform_changes && global_transform == last_transform) {
+ return;
+ }
+
if (area)
- Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
+ Physics2DServer::get_singleton()->area_set_transform(rid, global_transform);
else
- Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
+ Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform);
+
+ last_transform = global_transform;
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -318,6 +330,10 @@ void CollisionObject2D::_mouse_exit() {
emit_signal(SceneStringNames::get_singleton()->mouse_exited);
}
+void CollisionObject2D::set_only_update_transform_changes(bool p_enable) {
+ only_update_transform_changes = p_enable;
+}
+
void CollisionObject2D::_update_pickable() {
if (!is_inside_tree())
return;
@@ -384,6 +400,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
pickable = true;
set_notify_transform(true);
total_subshapes = 0;
+ only_update_transform_changes = false;
if (p_area) {
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 6da63d1a0b..29a00bd9f9 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -65,6 +65,8 @@ class CollisionObject2D : public Node2D {
int total_subshapes;
Map<uint32_t, ShapeData> shapes;
+ Transform2D last_transform;
+ bool only_update_transform_changes; //this is used for sync physics in KinematicBody
protected:
CollisionObject2D(RID p_rid, bool p_area);
@@ -78,6 +80,8 @@ protected:
void _mouse_enter();
void _mouse_exit();
+ void set_only_update_transform_changes(bool p_enable);
+
public:
uint32_t create_shape_owner(Object *p_owner);
void remove_shape_owner(uint32_t owner);
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index 329382c034..7d5360c0e4 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -158,8 +158,8 @@ void Joint2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint2D::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint2D::get_exclude_nodes_from_collision);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a"), "set_node_a", "get_node_a");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b"), "set_node_b", "get_node_b");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject2D"), "set_node_a", "get_node_a");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject2D"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bias", PROPERTY_HINT_RANGE, "0,0.9,0.001"), "set_bias", "get_bias");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_collision"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision");
}
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 9a44eb31bb..f93c7d1f79 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -59,14 +59,22 @@ bool Light2D::_edit_use_pivot() const {
Rect2 Light2D::_edit_get_rect() const {
if (texture.is_null())
- return Node2D::_edit_get_rect();
+ return Rect2();
Size2 s = texture->get_size() * _scale;
return Rect2(texture_offset - s / 2.0, s);
}
bool Light2D::_edit_use_rect() const {
- return true;
+ return !texture.is_null();
+}
+
+Rect2 Light2D::get_anchorable_rect() const {
+ if (texture.is_null())
+ return Rect2();
+
+ Size2 s = texture->get_size() * _scale;
+ return Rect2(texture_offset - s / 2.0, s);
}
void Light2D::_update_light_visibility() {
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 543805e329..40469cfbc8 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -94,6 +94,8 @@ public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 3e61dd05f4..e9e895b5bb 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -349,7 +349,7 @@ void Line2D::_bind_methods() {
ADD_GROUP("Fill", "");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile"), "set_texture_mode", "get_texture_mode");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile,Stretch"), "set_texture_mode", "get_texture_mode");
ADD_GROUP("Capping", "");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round"), "set_joint_mode", "get_joint_mode");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_begin_cap_mode", "get_begin_cap_mode");
@@ -368,6 +368,7 @@ void Line2D::_bind_methods() {
BIND_ENUM_CONSTANT(LINE_TEXTURE_NONE);
BIND_ENUM_CONSTANT(LINE_TEXTURE_TILE);
+ BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH);
ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed);
}
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 24c48982cd..6918018c12 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -52,8 +52,8 @@ public:
enum LineTextureMode {
LINE_TEXTURE_NONE = 0,
- LINE_TEXTURE_TILE
- // TODO STRETCH mode
+ LINE_TEXTURE_TILE,
+ LINE_TEXTURE_STRETCH
};
Line2D();
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index 845788bada..a3f1b25e05 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -146,7 +146,9 @@ void LineBuilder::build() {
float current_distance1 = 0.f;
float total_distance = 0.f;
_interpolate_color = gradient != NULL;
- bool distance_required = _interpolate_color || texture_mode == Line2D::LINE_TEXTURE_TILE;
+ bool distance_required = _interpolate_color ||
+ texture_mode == Line2D::LINE_TEXTURE_TILE ||
+ texture_mode == Line2D::LINE_TEXTURE_STRETCH;
if (distance_required)
total_distance = calculate_total_distance(points);
if (_interpolate_color)
@@ -170,7 +172,7 @@ void LineBuilder::build() {
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx0 = 0.5f / tile_aspect;
}
- new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f));
+ new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, fmin(uvx0 * 2, 1.f), 1.f));
total_distance += width;
current_distance0 += hw;
current_distance1 = current_distance0;
@@ -290,8 +292,10 @@ void LineBuilder::build() {
color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
}
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
- uvx0 = current_distance0 / (width * tile_aspect);
uvx1 = current_distance1 / (width * tile_aspect);
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ uvx0 = current_distance0 / total_distance;
+ uvx1 = current_distance1 / total_distance;
}
strip_add_quad(pos_up1, pos_down1, color1, uvx1);
@@ -301,7 +305,6 @@ void LineBuilder::build() {
u0 = u1;
f0 = f1;
pos0 = pos1;
- current_distance0 = current_distance1;
if (intersection_result == SEGMENT_INTERSECT) {
if (current_joint_mode == Line2D::LINE_JOINT_SHARP) {
pos_up0 = pos_up1;
@@ -378,6 +381,8 @@ void LineBuilder::build() {
}
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ uvx1 = current_distance1 / total_distance;
}
strip_add_quad(pos_up1, pos_down1, color1, uvx1);
@@ -386,7 +391,7 @@ void LineBuilder::build() {
if (end_cap_mode == Line2D::LINE_CAP_ROUND) {
// Note: color is not used in case we don't interpolate...
Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0);
- new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f, 0.f, 1.f, 1.f));
+ new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f / tile_aspect, 0.f, 1.0f / tile_aspect, 1.f));
}
}
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 3813bd96fe..7252602a93 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -130,7 +130,6 @@ void Node2D::_update_xform_values() {
void Node2D::_update_transform() {
- Transform2D mat(angle, pos);
_mat.set_rotation_and_scale(angle, _scale);
_mat.elements[2] = pos;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index feb11089d0..d5a61b0b6b 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -30,10 +30,11 @@
#include "physics_body_2d.h"
+#include "core/core_string_names.h"
#include "core/method_bind_ext.gen.inc"
#include "engine.h"
+#include "math_funcs.h"
#include "scene/scene_string_names.h"
-
void PhysicsBody2D::_notification(int p_what) {
/*
@@ -186,28 +187,75 @@ real_t StaticBody2D::get_constant_angular_velocity() const {
return constant_angular_velocity;
}
+#ifndef DISABLE_DEPRECATED
void StaticBody2D::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
+
real_t StaticBody2D::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void StaticBody2D::set_bounce(real_t p_bounce) {
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
+
real_t StaticBody2D::get_bounce() const {
- return bounce;
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
+
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const {
+ return physics_material_override;
}
void StaticBody2D::_bind_methods() {
@@ -222,23 +270,41 @@ void StaticBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody2D::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody2D::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody2D::_reload_physics_characteristics);
+
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
}
StaticBody2D::StaticBody2D() :
PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) {
constant_angular_velocity = 0;
- bounce = 0;
- friction = 1;
}
StaticBody2D::~StaticBody2D() {
}
+void StaticBody2D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0);
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, Physics2DServer::COMBINE_MODE_INHERIT);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, Physics2DServer::COMBINE_MODE_INHERIT);
+ } else {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, (Physics2DServer::CombineMode)physics_material_override->get_bounce_combine_mode());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, (Physics2DServer::CombineMode)physics_material_override->get_friction_combine_mode());
+ }
+}
+
void RigidBody2D::_body_enter_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
@@ -545,28 +611,72 @@ real_t RigidBody2D::get_weight() const {
return mass * real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10;
}
+#ifndef DISABLE_DEPRECATED
void RigidBody2D::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
real_t RigidBody2D::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void RigidBody2D::set_bounce(real_t p_bounce) {
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
real_t RigidBody2D::get_bounce() const {
- return bounce;
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
+
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const {
+ return physics_material_override;
}
void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) {
@@ -843,6 +953,11 @@ void RigidBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody2D::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody2D::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody2D::_reload_physics_characteristics);
+
ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale);
ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale);
@@ -903,6 +1018,7 @@ void RigidBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode");
@@ -941,9 +1057,7 @@ RigidBody2D::RigidBody2D() :
mode = MODE_RIGID;
- bounce = 0;
mass = 1;
- friction = 1;
gravity_scale = 1;
linear_damp = -1;
@@ -969,13 +1083,27 @@ RigidBody2D::~RigidBody2D() {
memdelete(contact_monitor);
}
+void RigidBody2D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0);
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, Physics2DServer::COMBINE_MODE_INHERIT);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, Physics2DServer::COMBINE_MODE_INHERIT);
+ } else {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, (Physics2DServer::CombineMode)physics_material_override->get_bounce_combine_mode());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, (Physics2DServer::CombineMode)physics_material_override->get_friction_combine_mode());
+ }
+}
+
//////////////////////////
-Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia) {
+Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) {
Collision col;
- if (move_and_collide(p_motion, p_infinite_inertia, col)) {
+ if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only)) {
if (motion_cache.is_null()) {
motion_cache.instance();
motion_cache->owner = this;
@@ -989,11 +1117,48 @@ Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p
return Ref<KinematicCollision2D>();
}
-bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision) {
+bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) {
+
+ Physics2DServer::SeparationResult sep_res[8]; //max 8 rays
+
+ Transform2D gt = get_global_transform();
+
+ Vector2 recover;
+ int hits = Physics2DServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin);
+ int deepest = -1;
+ float deepest_depth;
+ for (int i = 0; i < hits; i++) {
+ if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) {
+ deepest = i;
+ deepest_depth = sep_res[i].collision_depth;
+ }
+ }
+
+ gt.elements[2] += recover;
+ set_global_transform(gt);
+
+ if (deepest != -1) {
+ r_collision.collider = sep_res[deepest].collider_id;
+ r_collision.collider_metadata = sep_res[deepest].collider_metadata;
+ r_collision.collider_shape = sep_res[deepest].collider_shape;
+ r_collision.collider_vel = sep_res[deepest].collider_velocity;
+ r_collision.collision = sep_res[deepest].collision_point;
+ r_collision.normal = sep_res[deepest].collision_normal;
+ r_collision.local_shape = sep_res[deepest].collision_local_shape;
+ r_collision.travel = recover;
+ r_collision.remainder = Vector2();
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) {
Transform2D gt = get_global_transform();
Physics2DServer::MotionResult result;
- bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result);
+ bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes);
if (colliding) {
r_collision.collider_metadata = result.collider_metadata;
@@ -1002,23 +1167,36 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_
r_collision.collision = result.collision_point;
r_collision.normal = result.collision_normal;
r_collision.collider = result.collider_id;
+ r_collision.collider_rid = result.collider;
r_collision.travel = result.motion;
r_collision.remainder = result.remainder;
r_collision.local_shape = result.collision_local_shape;
}
- gt.elements[2] += result.motion;
- set_global_transform(gt);
+ if (!p_test_only) {
+ gt.elements[2] += result.motion;
+ set_global_transform(gt);
+ }
return colliding;
}
Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
- Vector2 motion = (floor_velocity + p_linear_velocity) * get_physics_process_delta_time();
+ Vector2 floor_motion = floor_velocity;
+ if (on_floor && on_floor_body.is_valid()) {
+ //this approach makes sure there is less delay between the actual body velocity and the one we saved
+ Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body);
+ if (bs) {
+ floor_motion = bs->get_linear_velocity();
+ }
+ }
+
+ Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time();
Vector2 lv = p_linear_velocity;
on_floor = false;
+ on_floor_body = RID();
on_ceiling = false;
on_wall = false;
colliders.clear();
@@ -1027,48 +1205,68 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
while (p_max_slides) {
Collision collision;
+ bool found_collision = false;
+
+ for (int i = 0; i < 2; i++) {
+ bool collided;
+ if (i == 0) { //collide
+ collided = move_and_collide(motion, p_infinite_inertia, collision);
+ if (!collided) {
+ motion = Vector2(); //clear because no collision happened and motion completed
+ }
+ } else { //separate raycasts (if any)
+ collided = separate_raycast_shapes(p_infinite_inertia, collision);
+ if (collided) {
+ collision.remainder = motion; //keep
+ collision.travel = Vector2();
+ }
+ }
- bool collided = move_and_collide(motion, p_infinite_inertia, collision);
-
- if (collided) {
-
- motion = collision.remainder;
-
- if (p_floor_direction == Vector2()) {
- //all is a wall
- on_wall = true;
- } else {
- if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+ if (collided) {
+ found_collision = true;
+ }
- on_floor = true;
- floor_velocity = collision.collider_vel;
+ if (collided) {
- Vector2 rel_v = lv - floor_velocity;
- Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v);
+ motion = collision.remainder;
- if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) {
- Transform2D gt = get_global_transform();
- gt.elements[2] -= collision.travel;
- set_global_transform(gt);
- return Vector2();
- }
- } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
- on_ceiling = true;
- } else {
+ if (p_floor_direction == Vector2()) {
+ //all is a wall
on_wall = true;
+ } else {
+ if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+
+ on_floor = true;
+ on_floor_body = collision.collider_rid;
+ floor_velocity = collision.collider_vel;
+
+ Vector2 rel_v = lv - floor_velocity;
+ Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v);
+
+ if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) {
+ Transform2D gt = get_global_transform();
+ gt.elements[2] -= collision.travel;
+ set_global_transform(gt);
+ return Vector2();
+ }
+ } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
+ on_ceiling = true;
+ } else {
+ on_wall = true;
+ }
}
- }
- Vector2 n = collision.normal;
- motion = motion.slide(n);
- lv = lv.slide(n);
+ Vector2 n = collision.normal;
+ motion = motion.slide(n);
+ lv = lv.slide(n);
- colliders.push_back(collision);
+ colliders.push_back(collision);
+ }
+ }
- } else {
+ if (!found_collision) {
break;
}
-
p_max_slides--;
if (motion == Vector2())
break;
@@ -1077,6 +1275,31 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
return lv;
}
+Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
+
+ bool was_on_floor = on_floor;
+
+ Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_slope_stop_min_velocity, p_max_slides, p_floor_max_angle);
+ if (!was_on_floor || p_snap == Vector2()) {
+ return ret;
+ }
+
+ Collision col;
+ Transform2D gt = get_global_transform();
+
+ if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) {
+ gt.elements[2] += col.travel;
+ if (p_floor_direction != Vector2() && Math::acos(p_floor_direction.normalized().dot(col.normal)) < p_floor_max_angle) {
+ on_floor = true;
+ on_floor_body = col.collider_rid;
+ floor_velocity = col.collider_vel;
+ }
+ set_global_transform(gt);
+ }
+
+ return ret;
+}
+
bool KinematicBody2D::is_on_floor() const {
return on_floor;
@@ -1138,10 +1361,60 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) {
return slide_colliders[p_bounce];
}
+void KinematicBody2D::set_sync_to_physics(bool p_enable) {
+
+ if (sync_to_physics == p_enable) {
+ return;
+ }
+ sync_to_physics = p_enable;
+ if (p_enable) {
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
+ set_only_update_transform_changes(true);
+ set_notify_local_transform(true);
+ } else {
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, "");
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
+ }
+}
+
+bool KinematicBody2D::is_sync_to_physics_enabled() const {
+ return sync_to_physics;
+}
+
+void KinematicBody2D::_direct_state_changed(Object *p_state) {
+
+ if (!sync_to_physics)
+ return;
+
+ Physics2DDirectBodyState *state = Object::cast_to<Physics2DDirectBodyState>(p_state);
+
+ last_valid_transform = state->get_transform();
+ set_notify_local_transform(false);
+ set_global_transform(last_valid_transform);
+ set_notify_local_transform(true);
+}
+
+void KinematicBody2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ last_valid_transform = get_global_transform();
+ }
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+ //used by sync to physics, send the new transform to the physics
+ Transform2D new_transform = get_global_transform();
+ Physics2DServer::get_singleton()->body_set_state(get_rid(), Physics2DServer::BODY_STATE_TRANSFORM, new_transform);
+ //but then revert changes
+ set_notify_local_transform(false);
+ set_global_transform(last_valid_transform);
+ set_notify_local_transform(true);
+ }
+}
void KinematicBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody2D::_move, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
+ ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move);
@@ -1156,7 +1429,13 @@ void KinematicBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody2D::get_slide_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody2D::_get_slide_collision);
+ ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &KinematicBody2D::set_sync_to_physics);
+ ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &KinematicBody2D::is_sync_to_physics_enabled);
+
+ ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody2D::_direct_state_changed);
+
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
}
KinematicBody2D::KinematicBody2D() :
@@ -1167,6 +1446,7 @@ KinematicBody2D::KinematicBody2D() :
on_floor = false;
on_ceiling = false;
on_wall = false;
+ sync_to_physics = false;
}
KinematicBody2D::~KinematicBody2D() {
if (motion_cache.is_valid()) {
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 0fda3c5c05..bd100f6228 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -32,6 +32,7 @@
#define PHYSICS_BODY_2D_H
#include "scene/2d/collision_object_2d.h"
+#include "scene/resources/physics_material.h"
#include "servers/physics_2d_server.h"
#include "vset.h"
@@ -79,18 +80,21 @@ class StaticBody2D : public PhysicsBody2D {
Vector2 constant_linear_velocity;
real_t constant_angular_velocity;
- real_t bounce;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
protected:
static void _bind_methods();
public:
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_constant_linear_velocity(const Vector2 &p_vel);
void set_constant_angular_velocity(real_t p_vel);
@@ -100,6 +104,9 @@ public:
StaticBody2D();
~StaticBody2D();
+
+private:
+ void _reload_physics_characteristics();
};
class RigidBody2D : public PhysicsBody2D {
@@ -125,9 +132,8 @@ private:
Physics2DDirectBodyState *state;
Mode mode;
- real_t bounce;
real_t mass;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
real_t gravity_scale;
real_t linear_damp;
real_t angular_damp;
@@ -204,11 +210,16 @@ public:
void set_weight(real_t p_weight);
real_t get_weight() const;
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
@@ -261,6 +272,9 @@ public:
RigidBody2D();
~RigidBody2D();
+
+private:
+ void _reload_physics_characteristics();
};
VARIANT_ENUM_CAST(RigidBody2D::Mode);
@@ -276,6 +290,7 @@ public:
Vector2 normal;
Vector2 collider_vel;
ObjectID collider;
+ RID collider_rid;
int collider_shape;
Variant collider_metadata;
Vector2 remainder;
@@ -287,29 +302,40 @@ private:
float margin;
Vector2 floor_velocity;
+ RID on_floor_body;
bool on_floor;
bool on_ceiling;
bool on_wall;
+ bool sync_to_physics;
+
Vector<Collision> colliders;
Vector<Ref<KinematicCollision2D> > slide_colliders;
Ref<KinematicCollision2D> motion_cache;
_FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const;
- Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true);
+ Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
+ Transform2D last_valid_transform;
+ void _direct_state_changed(Object *p_state);
+
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
- bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision);
+ bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
+
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia);
+ bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision);
+
void set_safe_margin(float p_margin);
float get_safe_margin() const;
Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
+ Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
@@ -318,6 +344,9 @@ public:
int get_slide_count() const;
Collision get_slide_collision(int p_bounce) const;
+ void set_sync_to_physics(bool p_enable);
+ bool is_sync_to_physics_enabled() const;
+
KinematicBody2D();
~KinematicBody2D();
};
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 4d6ebc81c3..81ed3c63c3 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -649,7 +649,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
ADD_GROUP("Skeleton", "");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton", "get_skeleton");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton");
ADD_GROUP("Invert", "invert_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert");
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index da764e032b..63c3d78dfd 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -201,7 +201,7 @@ void RemoteTransform2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_scale", "update_remote_scale"), &RemoteTransform2D::set_update_scale);
ClassDB::bind_method(D_METHOD("get_update_scale"), &RemoteTransform2D::get_update_scale);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path"), "set_remote_node", "get_remote_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_remote_node", "get_remote_node");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_coordinates"), "set_use_global_coordinates", "get_use_global_coordinates");
ADD_GROUP("Update", "update_");
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
index 9480f18176..45f63fd5bf 100644
--- a/scene/2d/screen_button.cpp
+++ b/scene/2d/screen_button.cpp
@@ -324,19 +324,21 @@ void TouchScreenButton::_release(bool p_exiting_tree) {
}
Rect2 TouchScreenButton::_edit_get_rect() const {
-
- if (texture.is_null())
- return Rect2(0, 0, 1, 1);
- /*
if (texture.is_null())
return CanvasItem::_edit_get_rect();
- */
return Rect2(Size2(), texture->get_size());
}
bool TouchScreenButton::_edit_use_rect() const {
- return true;
+ return !texture.is_null();
+}
+
+Rect2 TouchScreenButton::get_anchorable_rect() const {
+ if (texture.is_null())
+ return CanvasItem::get_anchorable_rect();
+
+ return Rect2(Size2(), texture->get_size());
}
void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h
index b2fafcc93d..3e8adc2e5e 100644
--- a/scene/2d/screen_button.h
+++ b/scene/2d/screen_button.h
@@ -103,8 +103,9 @@ public:
bool is_pressed() const;
- Rect2 _edit_get_rect() const;
+ virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
TouchScreenButton();
};
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 64d0771fab..ebe0e81f6e 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -63,9 +63,16 @@ Rect2 Sprite::_edit_get_rect() const {
}
bool Sprite::_edit_use_rect() const {
+ if (texture.is_null())
+ return false;
+
return true;
}
+Rect2 Sprite::get_anchorable_rect() const {
+ return get_rect();
+}
+
void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const {
Rect2 base_rect;
@@ -367,10 +374,6 @@ Rect2 Sprite::get_rect() const {
if (texture.is_null())
return Rect2(0, 0, 1, 1);
- /*
- if (texture.is_null())
- return CanvasItem::_edit_get_rect();
- */
Size2i s;
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index 609ad8bb34..0a5ff002cd 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -115,6 +115,7 @@ public:
int get_hframes() const;
Rect2 get_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
Sprite();
~Sprite();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 7fb4e36b63..72c3ed7425 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -62,7 +62,7 @@ void TileMap::_notification(int p_what) {
pending_update = true;
_recreate_quadrants();
- _update_dirty_quadrants();
+ update_dirty_quadrants();
RID space = get_world_2d()->get_space();
_update_quadrant_transform();
_update_quadrant_space(space);
@@ -245,7 +245,7 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
xform.elements[2].y += offset.y;
}
-void TileMap::_update_dirty_quadrants() {
+void TileMap::update_dirty_quadrants() {
if (!pending_update)
return;
@@ -721,7 +721,7 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool updat
return;
if (update) {
- _update_dirty_quadrants();
+ call_deferred("update_dirty_quadrants");
}
}
@@ -730,6 +730,11 @@ void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_
set_cell(p_pos.x, p_pos.y, p_tile, p_flip_x, p_flip_y, p_transpose);
}
+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"]);
+}
+
void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose, Vector2 p_autotile_coord) {
PosKey pk(p_x, p_y);
@@ -1021,7 +1026,7 @@ void TileMap::_recreate_quadrants() {
Q->get().cells.insert(E->key());
_make_quadrant_dirty(Q, false);
}
- _update_dirty_quadrants();
+ update_dirty_quadrants();
}
void TileMap::_clear_quadrants() {
@@ -1147,16 +1152,6 @@ PoolVector<int> TileMap::_get_tile_data() const {
return data;
}
-Rect2 TileMap::_edit_get_rect() const {
-
- const_cast<TileMap *>(this)->_update_dirty_quadrants();
- return rect_cache;
-}
-
-bool TileMap::_edit_use_rect() const {
- return true;
-}
-
void TileMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
@@ -1616,6 +1611,7 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell", "x", "y", "tile", "flip_x", "flip_y", "transpose", "autotile_coord"), &TileMap::set_cell, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("set_cellv", "position", "tile", "flip_x", "flip_y", "transpose"), &TileMap::set_cellv, DEFVAL(false), DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_celld", "data"), &TileMap::set_celld);
ClassDB::bind_method(D_METHOD("get_cell", "x", "y"), &TileMap::get_cell);
ClassDB::bind_method(D_METHOD("get_cellv", "position"), &TileMap::get_cellv);
ClassDB::bind_method(D_METHOD("is_cell_x_flipped", "x", "y"), &TileMap::is_cell_x_flipped);
@@ -1634,7 +1630,7 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_clear_quadrants"), &TileMap::_clear_quadrants);
ClassDB::bind_method(D_METHOD("_recreate_quadrants"), &TileMap::_recreate_quadrants);
- ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants);
+ ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants);
ClassDB::bind_method(D_METHOD("update_bitmask_area", "position"), &TileMap::update_bitmask_area);
ClassDB::bind_method(D_METHOD("update_bitmask_region", "start", "end"), &TileMap::update_bitmask_region, DEFVAL(Vector2()), DEFVAL(Vector2()));
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index d6774d42bb..c8aceac17d 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -191,7 +191,6 @@ private:
void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true);
void _recreate_quadrants();
void _clear_quadrants();
- void _update_dirty_quadrants();
void _update_quadrant_space(const RID &p_space);
void _update_quadrant_transform();
void _recompute_rect_cache();
@@ -241,18 +240,18 @@ public:
void set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord);
Vector2 get_cell_autotile_coord(int p_x, int p_y) const;
+ void set_celld(const Vector2 &p_pos, const Dictionary &p_data);
void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false);
int get_cellv(const Vector2 &p_pos) const;
- Rect2 _edit_get_rect() const;
- virtual bool _edit_use_rect() const;
-
void make_bitmask_area_dirty(const Vector2 &p_pos);
void update_bitmask_area(const Vector2 &p_pos);
void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2());
void update_cell_bitmask(int p_x, int p_y);
void update_dirty_bitmask();
+ void update_dirty_quadrants();
+
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index e7b3645001..5f0ac3dd80 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -35,11 +35,8 @@
#include "scene/main/viewport.h"
void AudioStreamPlayer3D::_mix_audio() {
- if (!stream_playback.is_valid()) {
- return;
- }
-
- if (!active) {
+ if (!stream_playback.is_valid() || !active ||
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -54,8 +51,13 @@ void AudioStreamPlayer3D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- //mix
- if (output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) {
+ if (stream_paused_fade_out) {
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
+ }
+
+ // Mix if we're not paused or we're fading out
+ if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) {
float output_pitch_scale = 0.0;
if (output_count) {
@@ -105,8 +107,10 @@ void AudioStreamPlayer3D::_mix_audio() {
int buffers = AudioServer::get_singleton()->get_channel_count();
for (int k = 0; k < buffers; k++) {
- AudioFrame vol_inc = (current.vol[k] - prev_outputs[i].vol[k]) / float(buffer_size);
- AudioFrame vol = current.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_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
@@ -188,6 +192,8 @@ void AudioStreamPlayer3D::_mix_audio() {
}
output_ready = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
@@ -237,6 +243,18 @@ void AudioStreamPlayer3D::_notification(int p_what) {
AudioServer::get_singleton()->remove_callback(_mix_audios, this);
}
+
+ if (p_what == NOTIFICATION_PAUSED) {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ }
+
+ if (p_what == NOTIFICATION_UNPAUSED) {
+ set_stream_paused(false);
+ }
+
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
@@ -552,7 +570,6 @@ void AudioStreamPlayer3D::_notification(int p_what) {
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
- ERR_FAIL_COND(!p_stream.is_valid());
AudioServer::get_singleton()->lock();
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
@@ -564,14 +581,15 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
setseek = -1;
}
- stream = p_stream;
- stream_playback = p_stream->instance_playback();
+ if (p_stream.is_valid()) {
+ stream = p_stream;
+ stream_playback = p_stream->instance_playback();
+ }
AudioServer::get_singleton()->unlock();
- if (stream_playback.is_null()) {
+ if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
- ERR_FAIL_COND(stream_playback.is_null());
}
}
@@ -825,6 +843,20 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
return doppler_tracking;
}
+void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
+
+ if (p_pause != stream_paused) {
+ stream_paused = p_pause;
+ stream_paused_fade_in = stream_paused ? false : true;
+ stream_paused_fade_out = stream_paused ? true : false;
+ }
+}
+
+bool AudioStreamPlayer3D::get_stream_paused() const {
+
+ return stream_paused;
+}
+
void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
@@ -888,6 +920,9 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &AudioStreamPlayer3D::set_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &AudioStreamPlayer3D::get_doppler_tracking);
+ 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("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -898,6 +933,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
@@ -949,9 +985,13 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() {
attenuation_filter_db = -24;
out_of_range_mode = OUT_OF_RANGE_MIX;
doppler_tracking = DOPPLER_TRACKING_DISABLED;
+ stream_paused = 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");
+ set_disable_scale(true);
}
AudioStreamPlayer3D::~AudioStreamPlayer3D() {
}
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 1fcb83cf21..14413d0702 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -108,6 +108,9 @@ private:
float max_db;
float pitch_scale;
bool autoplay;
+ bool stream_paused;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -199,6 +202,9 @@ public:
void set_doppler_tracking(DopplerTracking p_tracking);
DopplerTracking get_doppler_tracking() const;
+ void set_stream_paused(bool p_pause);
+ bool get_stream_paused() const;
+
AudioStreamPlayer3D();
~AudioStreamPlayer3D();
};
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index 204aaef7ec..26fd5ed658 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -811,4 +811,5 @@ BakedLightmap::BakedLightmap() {
propagation = 1;
hdr = false;
image_path = ".";
+ set_disable_scale(true);
}
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index e11e8abe5b..9a2046991b 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -613,6 +613,7 @@ Camera::Camera() {
velocity_tracker.instance();
doppler_tracking = DOPPLER_TRACKING_DISABLED;
set_notify_transform(true);
+ set_disable_scale(true);
}
Camera::~Camera() {
diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h
index f31d65e411..f8ef04b78f 100644
--- a/scene/3d/collision_object.h
+++ b/scene/3d/collision_object.h
@@ -39,6 +39,7 @@ class CollisionObject : public Spatial {
GDCLASS(CollisionObject, Spatial);
bool area;
+
RID rid;
struct ShapeData {
diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h
index c9c91a5824..6ca8e80ea1 100644
--- a/scene/3d/collision_shape.h
+++ b/scene/3d/collision_shape.h
@@ -49,6 +49,7 @@ class CollisionShape : public Spatial {
void resource_changed(RES res);
bool disabled;
+protected:
void _create_debug_shape();
void _update_in_shape_owner(bool p_xform_only = false);
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
new file mode 100644
index 0000000000..c3b0196316
--- /dev/null
+++ b/scene/3d/cpu_particles.cpp
@@ -0,0 +1,1417 @@
+#include "cpu_particles.h"
+
+#include "particles.h"
+#include "scene/3d/camera.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/surface_tool.h"
+#include "servers/visual_server.h"
+
+AABB CPUParticles::get_aabb() const {
+
+ return AABB();
+}
+PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const {
+
+ return PoolVector<Face3>();
+}
+
+void CPUParticles::set_emitting(bool p_emitting) {
+
+ emitting = p_emitting;
+ if (!is_processing_internal()) {
+ set_process_internal(true);
+ if (is_inside_tree()) {
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, true);
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ }
+ }
+}
+
+void CPUParticles::set_amount(int p_amount) {
+
+ ERR_FAIL_COND(p_amount < 1);
+
+ particles.resize(p_amount);
+ {
+ PoolVector<Particle>::Write w = particles.write();
+
+ for (int i = 0; i < p_amount; i++) {
+ w[i].active = false;
+ }
+ }
+
+ particle_data.resize((12 + 4 + 1) * p_amount);
+ VS::get_singleton()->multimesh_allocate(multimesh, p_amount, VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_8BIT, VS::MULTIMESH_CUSTOM_DATA_FLOAT);
+
+ particle_order.resize(p_amount);
+}
+void CPUParticles::set_lifetime(float p_lifetime) {
+
+ ERR_FAIL_COND(p_lifetime <= 0);
+ lifetime = p_lifetime;
+}
+
+void CPUParticles::set_one_shot(bool p_one_shot) {
+
+ one_shot = p_one_shot;
+}
+
+void CPUParticles::set_pre_process_time(float p_time) {
+
+ pre_process_time = p_time;
+}
+void CPUParticles::set_explosiveness_ratio(float p_ratio) {
+
+ explosiveness_ratio = p_ratio;
+}
+void CPUParticles::set_randomness_ratio(float p_ratio) {
+
+ randomness_ratio = p_ratio;
+}
+void CPUParticles::set_use_local_coordinates(bool p_enable) {
+
+ local_coords = p_enable;
+}
+void CPUParticles::set_speed_scale(float p_scale) {
+
+ speed_scale = p_scale;
+}
+
+bool CPUParticles::is_emitting() const {
+
+ return emitting;
+}
+int CPUParticles::get_amount() const {
+
+ return particles.size();
+}
+float CPUParticles::get_lifetime() const {
+
+ return lifetime;
+}
+bool CPUParticles::get_one_shot() const {
+
+ return one_shot;
+}
+
+float CPUParticles::get_pre_process_time() const {
+
+ return pre_process_time;
+}
+float CPUParticles::get_explosiveness_ratio() const {
+
+ return explosiveness_ratio;
+}
+float CPUParticles::get_randomness_ratio() const {
+
+ return randomness_ratio;
+}
+
+bool CPUParticles::get_use_local_coordinates() const {
+
+ return local_coords;
+}
+
+float CPUParticles::get_speed_scale() const {
+
+ return speed_scale;
+}
+
+void CPUParticles::set_draw_order(DrawOrder p_order) {
+
+ draw_order = p_order;
+}
+
+CPUParticles::DrawOrder CPUParticles::get_draw_order() const {
+
+ return draw_order;
+}
+
+void CPUParticles::set_mesh(const Ref<Mesh> &p_mesh) {
+
+ mesh = p_mesh;
+ if (mesh.is_valid()) {
+ VS::get_singleton()->multimesh_set_mesh(multimesh, mesh->get_rid());
+ } else {
+ VS::get_singleton()->multimesh_set_mesh(multimesh, RID());
+ }
+}
+
+Ref<Mesh> CPUParticles::get_mesh() const {
+
+ return mesh;
+}
+
+void CPUParticles::set_fixed_fps(int p_count) {
+ fixed_fps = p_count;
+}
+
+int CPUParticles::get_fixed_fps() const {
+ return fixed_fps;
+}
+
+void CPUParticles::set_fractional_delta(bool p_enable) {
+ fractional_delta = p_enable;
+}
+
+bool CPUParticles::get_fractional_delta() const {
+ return fractional_delta;
+}
+
+String CPUParticles::get_configuration_warning() const {
+
+ String warnings;
+
+ return warnings;
+}
+
+void CPUParticles::restart() {
+
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+
+ {
+ int pc = particles.size();
+ PoolVector<Particle>::Write w = particles.write();
+
+ for (int i = 0; i < pc; i++) {
+ w[i].active = false;
+ }
+ }
+}
+
+void CPUParticles::set_spread(float p_spread) {
+
+ spread = p_spread;
+}
+
+float CPUParticles::get_spread() const {
+
+ return spread;
+}
+
+void CPUParticles::set_flatness(float p_flatness) {
+
+ flatness = p_flatness;
+}
+float CPUParticles::get_flatness() const {
+
+ return flatness;
+}
+
+void CPUParticles::set_param(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+
+ parameters[p_param] = p_value;
+}
+float CPUParticles::get_param(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
+
+ return parameters[p_param];
+}
+
+void CPUParticles::set_param_randomness(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+
+ randomness[p_param] = p_value;
+}
+float CPUParticles::get_param_randomness(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
+
+ return randomness[p_param];
+}
+
+static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) {
+
+ Ref<Curve> curve = p_curve;
+ if (!curve.is_valid())
+ return;
+
+ curve->ensure_default_setup(p_min, p_max);
+}
+
+void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+
+ curve_parameters[p_param] = p_curve;
+
+ switch (p_param) {
+ case PARAM_INITIAL_LINEAR_VELOCITY: {
+ //do none for this one
+ } break;
+ case PARAM_ANGULAR_VELOCITY: {
+ _adjust_curve_range(p_curve, -360, 360);
+ } break;
+ /*case PARAM_ORBIT_VELOCITY: {
+ _adjust_curve_range(p_curve, -500, 500);
+ } break;*/
+ case PARAM_LINEAR_ACCEL: {
+ _adjust_curve_range(p_curve, -200, 200);
+ } break;
+ case PARAM_RADIAL_ACCEL: {
+ _adjust_curve_range(p_curve, -200, 200);
+ } break;
+ case PARAM_TANGENTIAL_ACCEL: {
+ _adjust_curve_range(p_curve, -200, 200);
+ } break;
+ case PARAM_DAMPING: {
+ _adjust_curve_range(p_curve, 0, 100);
+ } break;
+ case PARAM_ANGLE: {
+ _adjust_curve_range(p_curve, -360, 360);
+ } break;
+ case PARAM_SCALE: {
+
+ } break;
+ case PARAM_HUE_VARIATION: {
+ _adjust_curve_range(p_curve, -1, 1);
+ } break;
+ case PARAM_ANIM_SPEED: {
+ _adjust_curve_range(p_curve, 0, 200);
+ } break;
+ case PARAM_ANIM_OFFSET: {
+ } break;
+ default: {}
+ }
+}
+Ref<Curve> CPUParticles::get_param_curve(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Curve>());
+
+ return curve_parameters[p_param];
+}
+
+void CPUParticles::set_color(const Color &p_color) {
+
+ color = p_color;
+}
+
+Color CPUParticles::get_color() const {
+
+ return color;
+}
+
+void CPUParticles::set_color_ramp(const Ref<Gradient> &p_ramp) {
+
+ color_ramp = p_ramp;
+}
+
+Ref<Gradient> CPUParticles::get_color_ramp() const {
+
+ return color_ramp;
+}
+
+void CPUParticles::set_particle_flag(Flags p_flag, bool p_enable) {
+ ERR_FAIL_INDEX(p_flag, FLAG_MAX);
+ flags[p_flag] = p_enable;
+ if (p_flag == FLAG_DISABLE_Z) {
+ _change_notify();
+ }
+}
+
+bool CPUParticles::get_particle_flag(Flags p_flag) const {
+ ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
+ return flags[p_flag];
+}
+
+void CPUParticles::set_emission_shape(EmissionShape p_shape) {
+
+ emission_shape = p_shape;
+}
+
+void CPUParticles::set_emission_sphere_radius(float p_radius) {
+
+ emission_sphere_radius = p_radius;
+}
+
+void CPUParticles::set_emission_box_extents(Vector3 p_extents) {
+
+ emission_box_extents = p_extents;
+}
+
+void CPUParticles::set_emission_points(const PoolVector<Vector3> &p_points) {
+
+ emission_points = p_points;
+}
+
+void CPUParticles::set_emission_normals(const PoolVector<Vector3> &p_normals) {
+
+ emission_normals = p_normals;
+}
+
+void CPUParticles::set_emission_colors(const PoolVector<Color> &p_colors) {
+
+ emission_colors = p_colors;
+}
+
+float CPUParticles::get_emission_sphere_radius() const {
+
+ return emission_sphere_radius;
+}
+Vector3 CPUParticles::get_emission_box_extents() const {
+
+ return emission_box_extents;
+}
+PoolVector<Vector3> CPUParticles::get_emission_points() const {
+
+ return emission_points;
+}
+PoolVector<Vector3> CPUParticles::get_emission_normals() const {
+
+ return emission_normals;
+}
+
+PoolVector<Color> CPUParticles::get_emission_colors() const {
+
+ return emission_colors;
+}
+
+CPUParticles::EmissionShape CPUParticles::get_emission_shape() const {
+ return emission_shape;
+}
+void CPUParticles::set_gravity(const Vector3 &p_gravity) {
+
+ gravity = p_gravity;
+}
+
+Vector3 CPUParticles::get_gravity() const {
+
+ return gravity;
+}
+
+void CPUParticles::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "color" && color_ramp.is_valid()) {
+ property.usage = 0;
+ }
+
+ if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
+ property.usage = 0;
+ }
+
+ if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) {
+ property.usage = 0;
+ }
+
+ if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
+ property.usage = 0;
+ }
+
+ 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) {
+
+ x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b);
+ x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b);
+ x = (x >> uint32_t(16)) ^ x;
+ return x;
+}
+
+static float rand_from_seed(uint32_t &seed) {
+ int k;
+ int s = int(seed);
+ if (s == 0)
+ s = 305420679;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ seed = uint32_t(s);
+ return float(seed % uint32_t(65536)) / 65535.0;
+}
+
+float rand_from_seed_m1_p1(uint32_t &seed) {
+ return rand_from_seed(seed) * 2.0 - 1.0;
+}
+
+void CPUParticles::_particles_process(float p_delta) {
+
+ p_delta *= speed_scale;
+
+ int pcount = particles.size();
+ PoolVector<Particle>::Write w = particles.write();
+
+ Particle *parray = w.ptr();
+
+ float prev_time = time;
+ time += p_delta;
+ if (time > lifetime) {
+ time = Math::fmod(time, lifetime);
+ cycle++;
+ if (one_shot && cycle > 0) {
+ emitting = false;
+ }
+ }
+
+ Transform emission_xform;
+ Basis velocity_xform;
+ if (!local_coords) {
+ emission_xform = get_global_transform();
+ velocity_xform = emission_xform.basis.inverse().transposed();
+ }
+
+ for (int i = 0; i < pcount; i++) {
+
+ Particle &p = parray[i];
+
+ if (!emitting && !p.active)
+ continue;
+
+ float restart_time = (float(i) / float(pcount)) * lifetime;
+ float local_delta = p_delta;
+
+ if (randomness_ratio > 0.0) {
+ uint32_t seed = cycle;
+ if (restart_time >= time) {
+ 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_time *= (1.0 - explosiveness_ratio);
+ bool restart = false;
+
+ if (time > prev_time) {
+ // restart_time >= prev_time is used so particles emit in the first frame they are processed
+
+ if (restart_time >= prev_time && restart_time < time) {
+ restart = true;
+ if (fractional_delta) {
+ local_delta = (time - restart_time) * lifetime;
+ }
+ }
+
+ } else if (local_delta > 0.0) {
+ if (restart_time >= prev_time) {
+ restart = true;
+ if (fractional_delta) {
+ local_delta = (1.0 - restart_time + time) * lifetime;
+ }
+
+ } else if (restart_time < time) {
+ restart = true;
+ if (fractional_delta) {
+ local_delta = (time - restart_time) * lifetime;
+ }
+ }
+ }
+
+ if (restart) {
+
+ if (!emitting) {
+ p.active = false;
+ continue;
+ }
+ p.active = true;
+
+ /*float tex_linear_velocity = 0;
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0);
+ }*/
+
+ float tex_angle = 0.0;
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
+ tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(0);
+ }
+
+ float tex_anim_offset = 0.0;
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
+ tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(0);
+ }
+
+ p.seed = Math::rand();
+
+ p.angle_rand = Math::randf();
+ p.scale_rand = Math::randf();
+ p.hue_rot_rand = Math::randf();
+ p.anim_offset_rand = Math::randf();
+
+ float angle1_rad;
+ float angle2_rad;
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
+ Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0);
+ p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
+
+ } else {
+ //initiate velocity spread in 3D
+ angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
+ angle2_rad = (Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * Math_PI * spread / 180.0;
+
+ Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad));
+ Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad));
+ direction_yz.z = direction_yz.z / Math::sqrt(direction_yz.z); //better uniform distribution
+ Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);
+ direction.normalize();
+ p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
+ }
+
+ float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
+ p.custom[0] = Math::deg2rad(base_angle); //angle
+ p.custom[1] = 0.0; //phase
+ p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1)
+ p.transform = Transform();
+ p.time = 0;
+ p.base_color = Color(1, 1, 1, 1);
+
+ switch (emission_shape) {
+ case EMISSION_SHAPE_POINT: {
+ //do none
+ } break;
+ case EMISSION_SHAPE_SPHERE: {
+ p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius;
+ } break;
+ case EMISSION_SHAPE_BOX: {
+ p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_box_extents;
+ } break;
+ case EMISSION_SHAPE_POINTS:
+ case EMISSION_SHAPE_DIRECTED_POINTS: {
+
+ int pc = emission_points.size();
+ if (pc == 0)
+ break;
+
+ int random_idx = Math::rand() % pc;
+
+ p.transform.origin = emission_points.get(random_idx);
+
+ if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) {
+ if (flags[FLAG_DISABLE_Z]) {
+ /*
+ mat2 rotm;
+ ";
+ rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;
+ rotm[1] = rotm[0].yx * vec2(1.0, -1.0);
+ VELOCITY.xy = rotm * VELOCITY.xy;
+ */
+ } else {
+ Vector3 normal = emission_normals.get(random_idx);
+ Vector3 v0 = Math::abs(normal.z) < 0.999 ? Vector3(0.0, 0.0, 1.0) : Vector3(0, 1.0, 0.0);
+ Vector3 tangent = v0.cross(normal).normalized();
+ Vector3 bitangent = tangent.cross(normal).normalized();
+ Basis m3;
+ m3.set_axis(0, tangent);
+ m3.set_axis(1, bitangent);
+ m3.set_axis(2, normal);
+ p.velocity = m3.xform(p.velocity);
+ }
+ }
+
+ if (emission_colors.size() == pc) {
+ p.base_color = emission_colors.get(random_idx);
+ }
+ } break;
+ }
+
+ if (!local_coords) {
+ p.velocity = velocity_xform.xform(p.velocity);
+ p.transform = emission_xform * p.transform;
+ }
+
+ if (flags[FLAG_DISABLE_Z]) {
+ p.velocity.z = 0.0;
+ p.velocity.z = 0.0;
+ }
+
+ } else if (!p.active) {
+ continue;
+ } else {
+
+ uint32_t alt_seed = p.seed;
+
+ p.time += local_delta;
+ p.custom[1] = p.time / lifetime;
+
+ float tex_linear_velocity = 0.0;
+ 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_angular_velocity = 0.0;
+ if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
+ tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
+ }
+
+ float tex_linear_accel = 0.0;
+ if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
+ tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(p.custom[1]);
+ }
+
+ float tex_tangential_accel = 0.0;
+ if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
+ tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(p.custom[1]);
+ }
+
+ float tex_radial_accel = 0.0;
+ if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
+ tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(p.custom[1]);
+ }
+
+ float tex_damping = 0.0;
+ if (curve_parameters[PARAM_DAMPING].is_valid()) {
+ tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(p.custom[1]);
+ }
+
+ float tex_angle = 0.0;
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
+ tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(p.custom[1]);
+ }
+ float tex_anim_speed = 0.0;
+ if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) {
+ tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(p.custom[1]);
+ }
+
+ float tex_anim_offset = 0.0;
+ if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) {
+ tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(p.custom[1]);
+ }
+
+ Vector3 force = gravity;
+ Vector3 pos = p.transform.origin;
+ if (flags[FLAG_DISABLE_Z]) {
+ pos.z = 0.0;
+ }
+ //apply linear acceleration
+ force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3();
+ //apply radial acceleration
+ Vector3 org = emission_xform.origin;
+ Vector3 diff = pos - org;
+ force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3();
+ //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();
+
+ } else {
+ Vector3 crossDiff = diff.normalized().cross(gravity.normalized());
+ force += crossDiff.length() > 0.0 ? crossDiff.normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
+ }
+ //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;
+ }
+ }
+#endif
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ p.velocity = p.velocity.normalized() * tex_linear_velocity;
+ }
+ if (parameters[PARAM_DAMPING] + tex_damping > 0.0) {
+
+ float v = p.velocity.length();
+ float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]);
+ v -= damp * local_delta;
+ if (v < 0.0) {
+ p.velocity = Vector3();
+ } else {
+ p.velocity = p.velocity.normalized() * v;
+ }
+ }
+ float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
+ base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
+ p.custom[0] = Math::deg2rad(base_angle); //angle
+ p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle
+ if (flags[FLAG_ANIM_LOOP]) {
+ p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop
+
+ } else {
+ p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only
+ }
+ }
+ //apply color
+ //apply hue rotation
+
+ float tex_scale = 1.0;
+ if (curve_parameters[PARAM_SCALE].is_valid()) {
+ tex_scale = curve_parameters[PARAM_SCALE]->interpolate(p.custom[1]);
+ }
+
+ float tex_hue_variation = 0.0;
+ if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) {
+ tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(p.custom[1]);
+ }
+
+ float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_PI * 2.0 * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]);
+ float hue_rot_c = Math::cos(hue_rot_angle);
+ float hue_rot_s = Math::sin(hue_rot_angle);
+
+ Basis hue_rot_mat;
+ {
+ Basis mat1(0.299, 0.587, 0.114, 0.299, 0.587, 0.114, 0.299, 0.587, 0.114);
+ Basis mat2(0.701, -0.587, -0.114, -0.299, 0.413, -0.114, -0.300, -0.588, 0.886);
+ Basis mat3(0.168, 0.330, -0.497, -0.328, 0.035, 0.292, 1.250, -1.050, -0.203);
+
+ for (int j = 0; j < 3; j++) {
+ hue_rot_mat[j] = mat1[j] + mat2[j] * hue_rot_c + mat3[j] * hue_rot_s;
+ }
+ }
+
+ if (color_ramp.is_valid()) {
+ p.color = color_ramp->get_color_at_offset(p.custom[1]) * color;
+ } else {
+ p.color = color;
+ }
+
+ Vector3 color_rgb = hue_rot_mat.xform_inv(Vector3(p.color.r, p.color.g, p.color.b));
+ p.color.r = color_rgb.x;
+ p.color.g = color_rgb.y;
+ p.color.b = color_rgb.z;
+
+ p.color *= p.base_color;
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (p.velocity.length() > 0.0) {
+ p.transform.basis.set_axis(1, p.velocity.normalized());
+ } else {
+ p.transform.basis.set_axis(1, p.transform.basis.get_axis(1));
+ }
+ p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ p.transform.basis.set_axis(2, Vector3(0, 0, 1));
+
+ } else {
+ p.transform.basis.set_axis(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0));
+ p.transform.basis.set_axis(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0));
+ p.transform.basis.set_axis(2, Vector3(0, 0, 1));
+ }
+
+ } else {
+ //orient particle Y towards velocity
+ if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (p.velocity.length() > 0.0) {
+ p.transform.basis.set_axis(1, p.velocity.normalized());
+ } else {
+ p.transform.basis.set_axis(1, p.transform.basis.get_axis(1).normalized());
+ }
+ if (p.transform.basis.get_axis(1) == p.transform.basis.get_axis(0)) {
+ p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized());
+ } else {
+ p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized());
+ p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ }
+ } else {
+ p.transform.basis.orthonormalize();
+ }
+
+ //turn particle by rotation in Y
+ if (flags[FLAG_ROTATE_Y]) {
+ Basis rot_y(Vector3(0, 1, 0), p.custom[0]);
+ p.transform.basis = p.transform.basis * rot_y;
+ }
+ }
+
+ //scale by scale
+ float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
+ if (base_scale == 0.0) base_scale = 0.000001;
+
+ p.transform.basis.scale(Vector3(1, 1, 1) * base_scale);
+
+ if (flags[FLAG_DISABLE_Z]) {
+ p.velocity.z = 0.0;
+ p.transform.origin.z = 0.0;
+ }
+
+ p.transform.origin += p.velocity * local_delta;
+ }
+}
+
+void CPUParticles::_update_particle_data_buffer() {
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+
+ {
+
+ int pc = particles.size();
+
+ PoolVector<int>::Write ow;
+ int *order = NULL;
+
+ PoolVector<float>::Write w = particle_data.write();
+ 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();
+
+ for (int i = 0; i < pc; i++) {
+ order[i] = i;
+ }
+ if (draw_order == DRAW_ORDER_LIFETIME) {
+ SortArray<int, SortLifetime> sorter;
+ sorter.compare.particles = r.ptr();
+ sorter.sort(order, pc);
+ } else if (draw_order == DRAW_ORDER_VIEW_DEPTH) {
+ Camera *c = get_viewport()->get_camera();
+ if (c) {
+ Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close
+
+ if (local_coords) {
+ dir = un_transform.basis.xform(dir).normalized();
+ }
+
+ SortArray<int, SortAxis> sorter;
+ sorter.compare.particles = r.ptr();
+ sorter.compare.axis = dir;
+ sorter.sort(order, pc);
+ }
+ }
+ }
+
+ for (int i = 0; i < pc; i++) {
+
+ int idx = order ? order[i] : i;
+
+ Transform t = r[idx].transform;
+
+ if (!local_coords) {
+ t = un_transform * t;
+ }
+
+ // print_line(" particle " + itos(i) + ": " + String(r[idx].active ? "[x]" : "[ ]") + "\n\txform " + r[idx].transform + "\n\t" + r[idx].velocity + "\n\tcolor: " + r[idx].color);
+
+ if (r[idx].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);
+ }
+
+ Color c = r[idx].color;
+ uint8_t *data8 = (uint8_t *)&ptr[12];
+ data8[0] = CLAMP(c.r * 255.0, 0, 255);
+ data8[1] = CLAMP(c.g * 255.0, 0, 255);
+ data8[2] = CLAMP(c.b * 255.0, 0, 255);
+ data8[3] = CLAMP(c.a * 255.0, 0, 255);
+
+ ptr[13] = r[idx].custom[0];
+ ptr[14] = r[idx].custom[1];
+ ptr[15] = r[idx].custom[2];
+ ptr[16] = r[idx].custom[3];
+
+ ptr += 17;
+ }
+ }
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+}
+
+void CPUParticles::_update_render_thread() {
+
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+
+ VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data);
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+}
+
+void CPUParticles::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ if (is_processing_internal()) {
+
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, true);
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ if (is_processing_internal()) {
+
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, false);
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ }
+ }
+
+ if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
+ }
+
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+
+ if (particles.size() == 0)
+ return;
+
+ float delta = get_process_delta_time();
+ if (emitting) {
+
+ inactive_time = 0;
+ } else {
+ inactive_time += delta;
+ if (inactive_time > lifetime * 1.2) {
+ set_process_internal(false);
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, false);
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ //reset variables
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
+ }
+ }
+
+ if (time == 0 && pre_process_time > 0.0) {
+
+ float frame_time;
+ if (fixed_fps > 0)
+ frame_time = 1.0 / fixed_fps;
+ else
+ frame_time = 1.0 / 30.0;
+
+ float todo = pre_process_time;
+
+ while (todo >= 0) {
+ _particles_process(frame_time);
+ todo -= frame_time;
+ }
+ }
+
+ if (fixed_fps > 0) {
+ float frame_time = 1.0 / fixed_fps;
+ float decr = frame_time;
+
+ float ldelta = delta;
+ if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
+ ldelta = 0.1;
+ } else if (ldelta <= 0.0) { //unlikely but..
+ ldelta = 0.001;
+ }
+ float todo = frame_remainder + ldelta;
+
+ while (todo >= frame_time) {
+ _particles_process(frame_time);
+ todo -= decr;
+ }
+
+ frame_remainder = todo;
+
+ } else {
+ _particles_process(delta);
+ }
+
+ _update_particle_data_buffer();
+ }
+}
+
+void CPUParticles::convert_from_particles(Node *p_particles) {
+
+ Particles *particles = Object::cast_to<Particles>(p_particles);
+ ERR_FAIL_COND(!particles);
+
+ set_emitting(particles->is_emitting());
+ set_amount(particles->get_amount());
+ set_lifetime(particles->get_lifetime());
+ set_one_shot(particles->get_one_shot());
+ set_pre_process_time(particles->get_pre_process_time());
+ set_explosiveness_ratio(particles->get_explosiveness_ratio());
+ set_randomness_ratio(particles->get_randomness_ratio());
+ set_use_local_coordinates(particles->get_use_local_coordinates());
+ set_fixed_fps(particles->get_fixed_fps());
+ set_fractional_delta(particles->get_fractional_delta());
+ set_speed_scale(particles->get_speed_scale());
+ set_draw_order(DrawOrder(particles->get_draw_order()));
+ set_mesh(particles->get_draw_pass_mesh(0));
+
+ Ref<ParticlesMaterial> material = particles->get_process_material();
+ if (material.is_null())
+ return;
+
+ set_spread(material->get_spread());
+ set_flatness(material->get_flatness());
+
+ set_color(material->get_color());
+
+ Ref<GradientTexture> gt = material->get_color_ramp();
+ if (gt.is_valid()) {
+ set_color_ramp(gt->get_gradient());
+ }
+
+ set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
+ set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
+ set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
+ set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP));
+
+ set_emission_shape(EmissionShape(material->get_emission_shape()));
+ set_emission_sphere_radius(material->get_emission_sphere_radius());
+ set_emission_box_extents(material->get_emission_box_extents());
+
+ set_gravity(material->get_gravity());
+
+#define CONVERT_PARAM(m_param) \
+ set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \
+ { \
+ Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \
+ if (ctex.is_valid()) set_param_curve(m_param, ctex->get_curve()); \
+ } \
+ set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param));
+
+ CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY);
+ CONVERT_PARAM(PARAM_ANGULAR_VELOCITY);
+ // CONVERT_PARAM(PARAM_ORBIT_VELOCITY);
+ CONVERT_PARAM(PARAM_LINEAR_ACCEL);
+ CONVERT_PARAM(PARAM_RADIAL_ACCEL);
+ CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL);
+ CONVERT_PARAM(PARAM_DAMPING);
+ CONVERT_PARAM(PARAM_ANGLE);
+ CONVERT_PARAM(PARAM_SCALE);
+ CONVERT_PARAM(PARAM_HUE_VARIATION);
+ CONVERT_PARAM(PARAM_ANIM_SPEED);
+ CONVERT_PARAM(PARAM_ANIM_OFFSET);
+
+#undef CONVERT_PARAM
+}
+
+void CPUParticles::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles::set_emitting);
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles::set_amount);
+ ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &CPUParticles::set_lifetime);
+ ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &CPUParticles::set_one_shot);
+ ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles::set_pre_process_time);
+ ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles::set_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles::set_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles::set_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles::set_fixed_fps);
+ ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &CPUParticles::set_fractional_delta);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &CPUParticles::set_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles::is_emitting);
+ ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles::get_amount);
+ ClassDB::bind_method(D_METHOD("get_lifetime"), &CPUParticles::get_lifetime);
+ ClassDB::bind_method(D_METHOD("get_one_shot"), &CPUParticles::get_one_shot);
+ ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles::get_pre_process_time);
+ ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles::get_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles::get_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles::get_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles::get_fixed_fps);
+ ClassDB::bind_method(D_METHOD("get_fractional_delta"), &CPUParticles::get_fractional_delta);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &CPUParticles::get_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &CPUParticles::set_draw_order);
+
+ ClassDB::bind_method(D_METHOD("get_draw_order"), &CPUParticles::get_draw_order);
+
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CPUParticles::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles::get_mesh);
+
+ ClassDB::bind_method(D_METHOD("restart"), &CPUParticles::restart);
+
+ 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"), "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, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
+ 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", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+
+ BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
+ BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
+ BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH);
+
+ ////////////////////////////////
+
+ ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &CPUParticles::set_spread);
+ ClassDB::bind_method(D_METHOD("get_spread"), &CPUParticles::get_spread);
+
+ ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &CPUParticles::set_flatness);
+ ClassDB::bind_method(D_METHOD("get_flatness"), &CPUParticles::get_flatness);
+
+ ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &CPUParticles::set_param);
+ ClassDB::bind_method(D_METHOD("get_param", "param"), &CPUParticles::get_param);
+
+ ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &CPUParticles::set_param_randomness);
+ ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &CPUParticles::get_param_randomness);
+
+ ClassDB::bind_method(D_METHOD("set_param_curve", "param", "curve"), &CPUParticles::set_param_curve);
+ ClassDB::bind_method(D_METHOD("get_param_curve", "param"), &CPUParticles::get_param_curve);
+
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &CPUParticles::set_color);
+ ClassDB::bind_method(D_METHOD("get_color"), &CPUParticles::get_color);
+
+ ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles::set_color_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles::get_color_ramp);
+
+ ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles::set_particle_flag);
+ ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles::get_particle_flag);
+
+ ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles::set_emission_shape);
+ ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles::get_emission_shape);
+
+ ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &CPUParticles::set_emission_sphere_radius);
+ ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &CPUParticles::get_emission_sphere_radius);
+
+ ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &CPUParticles::set_emission_box_extents);
+ ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &CPUParticles::get_emission_box_extents);
+
+ ClassDB::bind_method(D_METHOD("set_emission_points", "array"), &CPUParticles::set_emission_points);
+ ClassDB::bind_method(D_METHOD("get_emission_points"), &CPUParticles::get_emission_points);
+
+ ClassDB::bind_method(D_METHOD("set_emission_normals", "array"), &CPUParticles::set_emission_normals);
+ ClassDB::bind_method(D_METHOD("get_emission_normals"), &CPUParticles::get_emission_normals);
+
+ ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles::set_emission_colors);
+ ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles::get_emission_colors);
+
+ ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles::get_gravity);
+ ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles::set_gravity);
+
+ ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles::convert_from_particles);
+
+ ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles::_update_render_thread);
+
+ ADD_GROUP("Emission Shape", "emission_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors");
+ ADD_GROUP("Flags", "flag_");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_particle_flag", "get_particle_flag", FLAG_DISABLE_Z);
+ ADD_GROUP("Spread", "");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness");
+ ADD_GROUP("Gravity", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
+ ADD_GROUP("Initial Velocity", "initial_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_GROUP("Angular Velocity", "angular_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "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);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL);
+ ADD_GROUP("Radial Accel", "radial_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL);
+ ADD_GROUP("Tangential Accel", "tangential_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL);
+ ADD_GROUP("Damping", "");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING);
+ ADD_GROUP("Angle", "");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE);
+ ADD_GROUP("Scale", "");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_SCALE);
+ ADD_GROUP("Color", "");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
+
+ ADD_GROUP("Hue Variation", "hue_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION);
+ ADD_GROUP("Animation", "anim_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP);
+
+ BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
+ BIND_ENUM_CONSTANT(PARAM_ANGULAR_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);
+ BIND_ENUM_CONSTANT(PARAM_DAMPING);
+ BIND_ENUM_CONSTANT(PARAM_ANGLE);
+ BIND_ENUM_CONSTANT(PARAM_SCALE);
+ BIND_ENUM_CONSTANT(PARAM_HUE_VARIATION);
+ BIND_ENUM_CONSTANT(PARAM_ANIM_SPEED);
+ BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET);
+ BIND_ENUM_CONSTANT(PARAM_MAX);
+
+ BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(FLAG_MAX);
+
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
+}
+
+CPUParticles::CPUParticles() {
+
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+
+ multimesh = VisualServer::get_singleton()->multimesh_create();
+ set_base(multimesh);
+
+ set_emitting(true);
+ set_one_shot(false);
+ set_amount(8);
+ set_lifetime(1);
+ set_fixed_fps(0);
+ set_fractional_delta(true);
+ set_pre_process_time(0);
+ set_explosiveness_ratio(0);
+ set_randomness_ratio(0);
+ set_use_local_coordinates(true);
+
+ set_draw_order(DRAW_ORDER_INDEX);
+ set_speed_scale(1);
+
+ set_spread(45);
+ set_flatness(0);
+ set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
+ //set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_LINEAR_ACCEL, 0);
+ set_param(PARAM_RADIAL_ACCEL, 0);
+ set_param(PARAM_TANGENTIAL_ACCEL, 0);
+ set_param(PARAM_DAMPING, 0);
+ set_param(PARAM_ANGLE, 0);
+ set_param(PARAM_SCALE, 1);
+ set_param(PARAM_HUE_VARIATION, 0);
+ set_param(PARAM_ANIM_SPEED, 0);
+ set_param(PARAM_ANIM_OFFSET, 0);
+ set_emission_shape(EMISSION_SHAPE_POINT);
+ set_emission_sphere_radius(1);
+ set_emission_box_extents(Vector3(1, 1, 1));
+
+ set_gravity(Vector3(0, -9.8, 0));
+
+ for (int i = 0; i < PARAM_MAX; i++) {
+ set_param_randomness(Parameter(i), 0);
+ }
+
+ for (int i = 0; i < FLAG_MAX; i++) {
+ flags[i] = false;
+ }
+
+ set_color(Color(1, 1, 1, 1));
+
+#ifndef NO_THREADS
+ update_mutex = Mutex::create();
+#endif
+}
+
+CPUParticles::~CPUParticles() {
+ VS::get_singleton()->free(multimesh);
+
+#ifndef NO_THREADS
+ memdelete(update_mutex);
+#endif
+}
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
new file mode 100644
index 0000000000..1ee709719d
--- /dev/null
+++ b/scene/3d/cpu_particles.h
@@ -0,0 +1,258 @@
+#ifndef CPU_PARTICLES_H
+#define CPU_PARTICLES_H
+#include "rid.h"
+#include "scene/3d/visual_instance.h"
+#include "scene/main/timer.h"
+#include "scene/resources/material.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class CPUParticles : public GeometryInstance {
+private:
+ GDCLASS(CPUParticles, GeometryInstance);
+
+public:
+ enum DrawOrder {
+ DRAW_ORDER_INDEX,
+ DRAW_ORDER_LIFETIME,
+ DRAW_ORDER_VIEW_DEPTH,
+ };
+
+ enum Parameter {
+
+ PARAM_INITIAL_LINEAR_VELOCITY,
+ PARAM_ANGULAR_VELOCITY,
+ //PARAM_ORBIT_VELOCITY,
+ PARAM_LINEAR_ACCEL,
+ PARAM_RADIAL_ACCEL,
+ PARAM_TANGENTIAL_ACCEL,
+ PARAM_DAMPING,
+ PARAM_ANGLE,
+ PARAM_SCALE,
+ PARAM_HUE_VARIATION,
+ PARAM_ANIM_SPEED,
+ PARAM_ANIM_OFFSET,
+ PARAM_MAX
+ };
+
+ enum Flags {
+ FLAG_ALIGN_Y_TO_VELOCITY,
+ FLAG_ROTATE_Y,
+ FLAG_DISABLE_Z,
+ FLAG_ANIM_LOOP,
+ FLAG_MAX
+ };
+
+ enum EmissionShape {
+ EMISSION_SHAPE_POINT,
+ EMISSION_SHAPE_SPHERE,
+ EMISSION_SHAPE_BOX,
+ EMISSION_SHAPE_POINTS,
+ EMISSION_SHAPE_DIRECTED_POINTS,
+ };
+
+private:
+ bool emitting;
+
+ struct Particle {
+ Transform transform;
+ Color color;
+ float custom[4];
+ Vector3 velocity;
+ bool active;
+ float angle_rand;
+ float scale_rand;
+ float hue_rot_rand;
+ float anim_offset_rand;
+ float time;
+ Color base_color;
+
+ uint32_t seed;
+ };
+
+ float time;
+ float inactive_time;
+ float frame_remainder;
+ int cycle;
+
+ RID multimesh;
+
+ PoolVector<Particle> particles;
+ PoolVector<float> particle_data;
+ PoolVector<int> particle_order;
+
+ struct SortLifetime {
+ const Particle *particles;
+
+ bool operator()(int p_a, int p_b) const {
+ return particles[p_a].time < particles[p_b].time;
+ }
+ };
+
+ struct SortAxis {
+ const Particle *particles;
+ Vector3 axis;
+ bool operator()(int p_a, int p_b) const {
+
+ return axis.dot(particles[p_a].transform.origin) < axis.dot(particles[p_b].transform.origin);
+ }
+ };
+
+ //
+
+ bool one_shot;
+
+ float lifetime;
+ float pre_process_time;
+ float explosiveness_ratio;
+ float randomness_ratio;
+ float speed_scale;
+ bool local_coords;
+ int fixed_fps;
+ bool fractional_delta;
+
+ DrawOrder draw_order;
+
+ Ref<Mesh> mesh;
+
+ ////////
+
+ float spread;
+ float flatness;
+
+ float parameters[PARAM_MAX];
+ float randomness[PARAM_MAX];
+
+ Ref<Curve> curve_parameters[PARAM_MAX];
+ Color color;
+ Ref<Gradient> color_ramp;
+
+ bool flags[FLAG_MAX];
+
+ EmissionShape emission_shape;
+ float emission_sphere_radius;
+ Vector3 emission_box_extents;
+ PoolVector<Vector3> emission_points;
+ PoolVector<Vector3> emission_normals;
+ PoolVector<Color> emission_colors;
+ int emission_point_count;
+
+ bool anim_loop;
+ Vector3 gravity;
+
+ void _particles_process(float p_delta);
+ void _update_particle_data_buffer();
+
+ Mutex *update_mutex;
+
+ void _update_render_thread();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+ virtual void _validate_property(PropertyInfo &property) const;
+
+public:
+ AABB get_aabb() const;
+ PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_emitting(bool p_emitting);
+ void set_amount(int p_amount);
+ void set_lifetime(float p_lifetime);
+ void set_one_shot(bool p_one_shot);
+ void set_pre_process_time(float p_time);
+ void set_explosiveness_ratio(float p_ratio);
+ void set_randomness_ratio(float p_ratio);
+ void set_visibility_aabb(const AABB &p_aabb);
+ void set_use_local_coordinates(bool p_enable);
+ void set_speed_scale(float p_scale);
+
+ bool is_emitting() const;
+ int get_amount() const;
+ float get_lifetime() const;
+ bool get_one_shot() const;
+ float get_pre_process_time() const;
+ float get_explosiveness_ratio() const;
+ float get_randomness_ratio() const;
+ AABB get_visibility_aabb() const;
+ bool get_use_local_coordinates() const;
+ float get_speed_scale() const;
+
+ void set_fixed_fps(int p_count);
+ int get_fixed_fps() const;
+
+ void set_fractional_delta(bool p_enable);
+ bool get_fractional_delta() const;
+
+ void set_draw_order(DrawOrder p_order);
+ DrawOrder get_draw_order() const;
+
+ void set_draw_passes(int p_count);
+ int get_draw_passes() const;
+
+ void set_mesh(const Ref<Mesh> &p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ ///////////////////
+
+ void set_spread(float p_spread);
+ float get_spread() const;
+
+ void set_flatness(float p_flatness);
+ float get_flatness() const;
+
+ void set_param(Parameter p_param, float p_value);
+ float get_param(Parameter p_param) const;
+
+ void set_param_randomness(Parameter p_param, float p_value);
+ float get_param_randomness(Parameter p_param) const;
+
+ void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve);
+ Ref<Curve> get_param_curve(Parameter p_param) const;
+
+ void set_color(const Color &p_color);
+ Color get_color() const;
+
+ void set_color_ramp(const Ref<Gradient> &p_texture);
+ Ref<Gradient> get_color_ramp() const;
+
+ void set_particle_flag(Flags p_flag, bool p_enable);
+ bool get_particle_flag(Flags p_flag) const;
+
+ void set_emission_shape(EmissionShape p_shape);
+ void set_emission_sphere_radius(float p_radius);
+ void set_emission_box_extents(Vector3 p_extents);
+ void set_emission_points(const PoolVector<Vector3> &p_points);
+ void set_emission_normals(const PoolVector<Vector3> &p_normals);
+ void set_emission_colors(const PoolVector<Color> &p_colors);
+ void set_emission_point_count(int p_count);
+
+ EmissionShape get_emission_shape() const;
+ float get_emission_sphere_radius() const;
+ Vector3 get_emission_box_extents() const;
+ PoolVector<Vector3> get_emission_points() const;
+ PoolVector<Vector3> get_emission_normals() const;
+ PoolVector<Color> get_emission_colors() const;
+ int get_emission_point_count() const;
+
+ void set_gravity(const Vector3 &p_gravity);
+ Vector3 get_gravity() const;
+
+ virtual String get_configuration_warning() const;
+
+ void restart();
+
+ void convert_from_particles(Node *p_particles);
+
+ CPUParticles();
+ ~CPUParticles();
+};
+
+VARIANT_ENUM_CAST(CPUParticles::DrawOrder)
+VARIANT_ENUM_CAST(CPUParticles::Parameter)
+VARIANT_ENUM_CAST(CPUParticles::Flags)
+VARIANT_ENUM_CAST(CPUParticles::EmissionShape)
+
+#endif // CPU_PARTICLES_H
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 4ad2eb60ee..6276d02eff 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -557,6 +557,7 @@ GIProbe::GIProbe() {
compress = false;
gi_probe = VS::get_singleton()->gi_probe_create();
+ set_disable_scale(true);
}
GIProbe::~GIProbe() {
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 7c42638107..16164cf3bf 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -306,6 +306,7 @@ Light::Light(VisualServer::LightType p_type) {
set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5);
set_param(PARAM_SHADOW_NORMAL_BIAS, 0.0);
set_param(PARAM_SHADOW_BIAS, 0.15);
+ set_disable_scale(true);
}
Light::Light() {
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 80bae911d4..722eadb9ca 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -36,6 +36,7 @@
#include "scene/resources/material.h"
#include "scene/scene_string_names.h"
#include "skeleton.h"
+
bool MeshInstance::_set(const StringName &p_name, const Variant &p_value) {
//this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else.
@@ -371,7 +372,7 @@ void MeshInstance::_bind_methods() {
ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton_path", "get_skeleton_path");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
}
MeshInstance::MeshInstance() {
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 5d359cd4d5..0dfec538f9 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -41,6 +41,7 @@ class MeshInstance : public GeometryInstance {
GDCLASS(MeshInstance, GeometryInstance);
+protected:
Ref<Mesh> mesh;
NodePath skeleton_path;
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 5056fb2fe4..7529a0e524 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -30,6 +30,7 @@
#include "physics_body.h"
+#include "core/core_string_names.h"
#include "engine.h"
#include "method_bind_ext.gen.inc"
#include "scene/scene_string_names.h"
@@ -121,23 +122,23 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const {
void PhysicsBody::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
- PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
}
- ERR_FAIL_COND(!physics_body);
- PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid());
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid());
}
void PhysicsBody::remove_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
- PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
}
- ERR_FAIL_COND(!physics_body);
- PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid());
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
void PhysicsBody::_set_layers(uint32_t p_mask) {
@@ -178,28 +179,75 @@ PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) :
collision_mask = 1;
}
+#ifndef DISABLE_DEPRECATED
void StaticBody::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
+
real_t StaticBody::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void StaticBody::set_bounce(real_t p_bounce) {
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
+
real_t StaticBody::get_bounce() const {
- return bounce;
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
+
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void StaticBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> StaticBody::get_physics_material_override() const {
+ return physics_material_override;
}
void StaticBody::set_constant_linear_velocity(const Vector3 &p_vel) {
@@ -236,24 +284,39 @@ void StaticBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics);
+
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody::remove_collision_exception_with);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
-
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
}
StaticBody::StaticBody() :
PhysicsBody(PhysicsServer::BODY_MODE_STATIC) {
-
- bounce = 0;
- friction = 1;
}
-StaticBody::~StaticBody() {
+StaticBody::~StaticBody() {}
+
+void StaticBody::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, PhysicsServer::COMBINE_MODE_INHERIT);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, PhysicsServer::COMBINE_MODE_INHERIT);
+ } else {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce_combine_mode());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction_combine_mode());
+ }
}
void RigidBody::_body_enter_tree(ObjectID p_id) {
@@ -550,28 +613,67 @@ real_t RigidBody::get_weight() const {
return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8));
}
+#ifndef DISABLE_DEPRECATED
void RigidBody::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
real_t RigidBody::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void RigidBody::set_bounce(real_t p_bounce) {
-
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
real_t RigidBody::get_bounce() const {
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
- return bounce;
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void RigidBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> RigidBody::get_physics_material_override() const {
+ return physics_material_override;
}
void RigidBody::set_gravity_scale(real_t p_gravity_scale) {
@@ -812,6 +914,11 @@ void RigidBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody::_reload_physics_characteristics);
+
ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody::set_linear_velocity);
ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody::get_linear_velocity);
@@ -865,6 +972,7 @@ void RigidBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection");
@@ -903,9 +1011,7 @@ RigidBody::RigidBody() :
mode = MODE_RIGID;
- bounce = 0;
mass = 1;
- friction = 1;
max_contacts_reported = 0;
state = NULL;
@@ -929,6 +1035,21 @@ RigidBody::~RigidBody() {
if (contact_monitor)
memdelete(contact_monitor);
}
+
+void RigidBody::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, PhysicsServer::COMBINE_MODE_INHERIT);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, PhysicsServer::COMBINE_MODE_INHERIT);
+ } else {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce_combine_mode());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction_combine_mode());
+ }
+}
+
//////////////////////////////////////////////////////
//////////////////////////
@@ -979,7 +1100,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in
return colliding;
}
-Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
+Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
Vector3 lv = p_linear_velocity;
@@ -1128,7 +1249,7 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) {
void KinematicBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_slides", "floor_max_angle"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
+ ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move);
@@ -2228,6 +2349,7 @@ void PhysicalBone::set_bounce(real_t p_bounce) {
bounce = p_bounce;
PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
}
+
real_t PhysicalBone::get_bounce() const {
return bounce;
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index 17d2769c79..44d6502be1 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -32,6 +32,7 @@
#define PHYSICS_BODY__H
#include "scene/3d/collision_object.h"
+#include "scene/resources/physics_material.h"
#include "servers/physics_server.h"
#include "skeleton.h"
#include "vset.h"
@@ -81,18 +82,22 @@ class StaticBody : public PhysicsBody {
Vector3 constant_linear_velocity;
Vector3 constant_angular_velocity;
- real_t bounce;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
protected:
static void _bind_methods();
public:
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_constant_linear_velocity(const Vector3 &p_vel);
void set_constant_angular_velocity(const Vector3 &p_vel);
@@ -102,6 +107,9 @@ public:
StaticBody();
~StaticBody();
+
+private:
+ void _reload_physics_characteristics();
};
class RigidBody : public PhysicsBody {
@@ -121,9 +129,8 @@ protected:
PhysicsDirectBodyState *state;
Mode mode;
- real_t bounce;
real_t mass;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
Vector3 linear_velocity;
Vector3 angular_velocity;
@@ -196,11 +203,16 @@ public:
void set_weight(real_t p_weight);
real_t get_weight() const;
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_linear_velocity(const Vector3 &p_velocity);
Vector3 get_linear_velocity() const;
@@ -249,6 +261,9 @@ public:
RigidBody();
~RigidBody();
+
+private:
+ void _reload_physics_characteristics();
};
VARIANT_ENUM_CAST(RigidBody::Mode);
@@ -294,7 +309,7 @@ protected:
static void _bind_methods();
public:
- bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision);
+ bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz);
bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia);
void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock);
@@ -303,7 +318,7 @@ public:
void set_safe_margin(float p_margin);
float get_safe_margin() const;
- Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
+ Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp
index b2d10006f7..7988c43eab 100644
--- a/scene/3d/physics_joint.cpp
+++ b/scene/3d/physics_joint.cpp
@@ -154,8 +154,8 @@ void Joint::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint::get_exclude_nodes_from_collision);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a"), "set_node_a", "get_node_a");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b"), "set_node_b", "get_node_b");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject"), "set_node_a", "get_node_a");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::INT, "solver/priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision/exclude_nodes"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision");
@@ -260,7 +260,7 @@ void HingeJoint::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_lower_limit", "lower_limit"), &HingeJoint::_set_lower_limit);
ClassDB::bind_method(D_METHOD("_get_lower_limit"), &HingeJoint::_get_lower_limit);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "params/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"), "set_param", "get_param", PARAM_BIAS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "params/bias", PROPERTY_HINT_RANGE, "0.00,0.99,0.01"), "set_param", "get_param", PARAM_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit/enable"), "set_flag", "get_flag", FLAG_USE_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit/upper", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_upper_limit", "_get_upper_limit");
@@ -270,7 +270,7 @@ void HingeJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_limit/relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_RELAXATION);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "motor/enable"), "set_flag", "get_flag", FLAG_ENABLE_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/target_velocity", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_lesser"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/max_impulse", PROPERTY_HINT_RANGE, "0.01,1024,0.01"), "set_param", "get_param", PARAM_MOTOR_MAX_IMPULSE);
BIND_ENUM_CONSTANT(PARAM_BIAS);
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index 4d50945062..fe522bbe97 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -274,6 +274,7 @@ ReflectionProbe::ReflectionProbe() {
probe = VisualServer::get_singleton()->reflection_probe_create();
VS::get_singleton()->instance_set_base(get_instance(), probe);
+ set_disable_scale(true);
}
ReflectionProbe::~ReflectionProbe() {
diff --git a/scene/3d/remote_transform.cpp b/scene/3d/remote_transform.cpp
index afb85f7314..2156e24cd0 100644
--- a/scene/3d/remote_transform.cpp
+++ b/scene/3d/remote_transform.cpp
@@ -194,7 +194,7 @@ void RemoteTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_scale", "update_remote_scale"), &RemoteTransform::set_update_scale);
ClassDB::bind_method(D_METHOD("get_update_scale"), &RemoteTransform::get_update_scale);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path"), "set_remote_node", "get_remote_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Spatial"), "set_remote_node", "get_remote_node");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_coordinates"), "set_use_global_coordinates", "get_use_global_coordinates");
ADD_GROUP("Update", "update_");
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index 76d90dc6ff..8d91b6f09f 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -547,6 +547,8 @@ void Skeleton::localize_rests() {
}
}
+#ifndef _3D_DISABLED
+
void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) {
ERR_FAIL_INDEX(p_bone, bones.size());
ERR_FAIL_COND(bones[p_bone].physical_bone);
@@ -691,6 +693,8 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
_physical_bones_add_remove_collision_exception(false, this, p_exception);
}
+#endif // _3D_DISABLED
+
void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@@ -727,11 +731,15 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
+#ifndef _3D_DISABLED
+
ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton::physical_bones_start_simulation_on, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception);
ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception);
+#endif // _3D_DISABLED
+
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index dad11960a5..9672acb57a 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -38,7 +38,10 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
+#ifndef _3D_DISABLED
class PhysicalBone;
+#endif // _3D_DISABLED
+
class Skeleton : public Spatial {
GDCLASS(Skeleton, Spatial);
@@ -64,8 +67,10 @@ class Skeleton : public Spatial {
Transform transform_final;
+#ifndef _3D_DISABLED
PhysicalBone *physical_bone;
PhysicalBone *cache_parent_physical_bone;
+#endif // _3D_DISABLED
List<uint32_t> nodes_bound;
@@ -75,8 +80,10 @@ class Skeleton : public Spatial {
ignore_animation = false;
custom_pose_enable = false;
disable_rest = false;
+#ifndef _3D_DISABLED
physical_bone = NULL;
cache_parent_physical_bone = NULL;
+#endif // _3D_DISABLED
}
};
@@ -164,6 +171,7 @@ public:
void localize_rests(); // used for loaders and tools
+#ifndef _3D_DISABLED
// Physical bone API
void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone);
@@ -182,6 +190,7 @@ public:
void physical_bones_start_simulation_on(const Array &p_bones);
void physical_bones_add_collision_exception(RID p_exception);
void physical_bones_remove_collision_exception(RID p_exception);
+#endif // _3D_DISABLED
public:
Skeleton();
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
new file mode 100644
index 0000000000..8498dc34c0
--- /dev/null
+++ b/scene/3d/soft_body.cpp
@@ -0,0 +1,740 @@
+/*************************************************************************/
+/* soft_physics_body.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 "soft_body.h"
+#include "os/os.h"
+#include "scene/3d/collision_object.h"
+#include "scene/3d/skeleton.h"
+#include "servers/physics_server.h"
+
+SoftBodyVisualServerHandler::SoftBodyVisualServerHandler() {}
+
+void SoftBodyVisualServerHandler::prepare(RID p_mesh, int p_surface) {
+ clear();
+
+ ERR_FAIL_COND(!p_mesh.is_valid());
+
+ mesh = p_mesh;
+ surface = p_surface;
+
+ const uint32_t surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, surface);
+ const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface);
+ const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface);
+ uint32_t surface_offsets[VS::ARRAY_MAX];
+
+ buffer = VS::get_singleton()->mesh_surface_get_array(mesh, surface);
+ stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets);
+ offset_vertices = surface_offsets[VS::ARRAY_VERTEX];
+ offset_normal = surface_offsets[VS::ARRAY_NORMAL];
+}
+
+void SoftBodyVisualServerHandler::clear() {
+
+ if (mesh.is_valid()) {
+ buffer.resize(0);
+ }
+
+ mesh = RID();
+}
+
+void SoftBodyVisualServerHandler::open() {
+ write_buffer = buffer.write();
+}
+
+void SoftBodyVisualServerHandler::close() {
+ write_buffer = PoolVector<uint8_t>::Write();
+}
+
+void SoftBodyVisualServerHandler::commit_changes() {
+ VS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer);
+}
+
+void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) {
+ copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3);
+}
+
+void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) {
+ copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3);
+}
+
+void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) {
+ VS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb);
+}
+
+SoftBody::PinnedPoint::PinnedPoint() :
+ point_index(-1),
+ spatial_attachment(NULL) {
+}
+
+SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
+ point_index = obj_tocopy.point_index;
+ spatial_attachment_path = obj_tocopy.spatial_attachment_path;
+ spatial_attachment = obj_tocopy.spatial_attachment;
+ vertex_offset_transform = obj_tocopy.vertex_offset_transform;
+}
+
+void SoftBody::_update_pickable() {
+ if (!is_inside_tree())
+ return;
+ bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree();
+ PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable);
+}
+
+bool SoftBody::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ String which = name.get_slicec('/', 0);
+
+ if ("pinned_points" == which) {
+
+ return _set_property_pinned_points_indices(p_value);
+
+ } else if ("attachments" == which) {
+
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ return _set_property_pinned_points_attachment(idx, what, p_value);
+ }
+
+ return false;
+}
+
+bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const {
+ String name = p_name;
+ String which = name.get_slicec('/', 0);
+
+ if ("pinned_points" == which) {
+ Array arr_ret;
+ const int pinned_points_indices_size = pinned_points_indices.size();
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ arr_ret.resize(pinned_points_indices_size);
+
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ arr_ret[i] = r[i].point_index;
+ }
+
+ r_ret = arr_ret;
+ return true;
+
+ } else if ("attachments" == which) {
+
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ return _get_property_pinned_points(idx, what, r_ret);
+ }
+
+ return false;
+}
+
+void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ const int pinned_points_indices_size = pinned_points_indices.size();
+
+ p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points"));
+
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index"));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path"));
+ }
+}
+
+bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) {
+
+ const int p_indices_size = p_indices.size();
+
+ { // Remove the pined points on physics server that will be removed by resize
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ if (p_indices_size < pinned_points_indices.size()) {
+ for (int i = pinned_points_indices.size() - 1; i >= p_indices_size; --i) {
+ pin_point(r[i].point_index, false);
+ }
+ }
+ }
+
+ pinned_points_indices.resize(p_indices_size);
+
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ int point_index;
+ for (int i = 0; i < p_indices_size; ++i) {
+ point_index = p_indices.get(i);
+ if (w[i].point_index != point_index) {
+ if (-1 != w[i].point_index)
+ pin_point(w[i].point_index, false);
+ w[i].point_index = point_index;
+ pin_point(w[i].point_index, true);
+ }
+ }
+ return true;
+}
+
+bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) {
+ if (pinned_points_indices.size() <= p_item) {
+ return false;
+ }
+
+ if ("spatial_attachment_path" == p_what) {
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ pin_point(w[p_item].point_index, true, p_value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const {
+ if (pinned_points_indices.size() <= p_item) {
+ return false;
+ }
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+
+ if ("point_index" == p_what) {
+ r_ret = r[p_item].point_index;
+ } else if ("spatial_attachment_path" == p_what) {
+ r_ret = r[p_item].spatial_attachment_path;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) {
+#ifdef TOOLS_ENABLED
+ if (p_changed == this) {
+ update_configuration_warning();
+ }
+#endif
+}
+
+void SoftBody::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_WORLD: {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ add_change_receptor(this);
+
+ RID space = get_world()->get_space();
+ PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space);
+ PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
+ update_physics_server();
+ } break;
+ case NOTIFICATION_READY: {
+ if (!parent_collision_ignore.is_empty())
+ add_collision_exception_with(get_node(parent_collision_ignore));
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (!simulation_started) {
+ PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
+
+ _update_cache_pin_points_datas();
+ // Submit bone attachment
+ const int pinned_points_indices_size = pinned_points_indices.size();
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ if (!r[i].spatial_attachment) {
+ // Use soft body position to update the point position
+ PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (get_global_transform() * r[i].vertex_offset_transform).origin);
+ } else {
+ PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (r[i].spatial_attachment->get_global_transform() * r[i].vertex_offset_transform).origin);
+ }
+ }
+ }
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+
+ _update_pickable();
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, RID());
+
+ } break;
+ }
+
+#ifdef TOOLS_ENABLED
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ update_configuration_warning();
+ }
+ }
+
+#endif
+}
+
+void SoftBody::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_draw_soft_mesh"), &SoftBody::_draw_soft_mesh);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody::set_collision_layer);
+ ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody::get_collision_layer);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody::set_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody::get_collision_mask_bit);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody::set_collision_layer_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody::get_collision_layer_bit);
+
+ ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore);
+ ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore);
+
+ ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with);
+ ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with);
+
+ ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody::set_simulation_precision);
+ ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody::get_simulation_precision);
+
+ ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody::set_total_mass);
+ ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody::get_total_mass);
+
+ ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody::set_linear_stiffness);
+ ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody::get_linear_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody::set_areaAngular_stiffness);
+ ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody::get_areaAngular_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody::set_volume_stiffness);
+ ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody::get_volume_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody::set_pressure_coefficient);
+ ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody::get_pressure_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody::set_pose_matching_coefficient);
+ ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody::get_pose_matching_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody::set_damping_coefficient);
+ ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody::get_damping_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody::set_drag_coefficient);
+ ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody::get_drag_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody::set_ray_pickable);
+ ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody::is_ray_pickable);
+
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient");
+}
+
+String SoftBody::get_configuration_warning() const {
+
+ String warning = MeshInstance::get_configuration_warning();
+
+ if (get_mesh().is_null()) {
+ if (!warning.empty())
+ warning += "\n\n";
+
+ warning += TTR("This body will be ignored until you set a mesh");
+ }
+
+ Transform t = get_transform();
+ if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) {
+ if (!warning.empty())
+ warning += "\n\n";
+
+ warning += TTR("Size changes to SoftBody will be overriden by the physics engine when running.\nChange the size in children collision shapes instead.");
+ }
+
+ return warning;
+}
+
+void SoftBody::_draw_soft_mesh() {
+ if (get_mesh().is_null())
+ return;
+
+ if (!visual_server_handler.is_ready()) {
+
+ visual_server_handler.prepare(get_mesh()->get_rid(), 0);
+
+ /// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
+ simulation_started = true;
+ call_deferred("set_as_toplevel", true);
+ call_deferred("set_transform", Transform());
+ }
+
+ visual_server_handler.open();
+ PhysicsServer::get_singleton()->soft_body_update_visual_server(physics_rid, &visual_server_handler);
+ visual_server_handler.close();
+
+ visual_server_handler.commit_changes();
+}
+
+void SoftBody::update_physics_server() {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ return;
+
+ if (get_mesh().is_valid()) {
+
+ become_mesh_owner();
+ PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
+ VS::get_singleton()->connect("frame_pre_draw", this, "_draw_soft_mesh");
+ } else {
+
+ PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL);
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh");
+ }
+}
+
+void SoftBody::become_mesh_owner() {
+ if (mesh.is_null())
+ return;
+
+ if (!mesh_owner) {
+ mesh_owner = true;
+
+ ERR_FAIL_COND(!mesh->get_surface_count());
+
+ // Get current mesh array and create new mesh array with necessary flag for softbody
+ Array surface_arrays = mesh->surface_get_arrays(0);
+ Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0);
+ uint32_t surface_format = mesh->surface_get_format(0);
+
+ surface_format &= ~(Mesh::ARRAY_COMPRESS_VERTEX | Mesh::ARRAY_COMPRESS_NORMAL);
+ surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
+
+ Ref<ArrayMesh> soft_mesh;
+ soft_mesh.instance();
+ soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format);
+
+ set_mesh(soft_mesh);
+
+ Vector<Ref<Material> > copy_materials;
+ copy_materials.append_array(materials);
+ for (int i = copy_materials.size() - 1; 0 <= i; --i) {
+ set_surface_material(i, copy_materials[i]);
+ }
+ }
+}
+
+void SoftBody::set_collision_mask(uint32_t p_mask) {
+ collision_mask = p_mask;
+ PhysicsServer::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask);
+}
+
+uint32_t SoftBody::get_collision_mask() const {
+ return collision_mask;
+}
+void SoftBody::set_collision_layer(uint32_t p_layer) {
+ collision_layer = p_layer;
+ PhysicsServer::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer);
+}
+
+uint32_t SoftBody::get_collision_layer() const {
+ return collision_layer;
+}
+
+void SoftBody::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 SoftBody::get_collision_mask_bit(int p_bit) const {
+ return get_collision_mask() & (1 << p_bit);
+}
+
+void SoftBody::set_collision_layer_bit(int p_bit, bool p_value) {
+ uint32_t layer = get_collision_layer();
+ if (p_value)
+ layer |= 1 << p_bit;
+ else
+ layer &= ~(1 << p_bit);
+ set_collision_layer(layer);
+}
+
+bool SoftBody::get_collision_layer_bit(int p_bit) const {
+ return get_collision_layer() & (1 << p_bit);
+}
+
+void SoftBody::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) {
+ parent_collision_ignore = p_parent_collision_ignore;
+}
+
+const NodePath &SoftBody::get_parent_collision_ignore() const {
+ return parent_collision_ignore;
+}
+
+void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) {
+ pinned_points_indices = p_pinned_points_indices;
+ PoolVector<PinnedPoint>::Read w = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+ pin_point(p_pinned_points_indices[i].point_index, true);
+ }
+}
+
+PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() {
+ return pinned_points_indices;
+}
+
+void SoftBody::add_collision_exception_with(Node *p_node) {
+ ERR_FAIL_NULL(p_node);
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
+ }
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid());
+}
+
+void SoftBody::remove_collision_exception_with(Node *p_node) {
+ ERR_FAIL_NULL(p_node);
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
+ }
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid());
+}
+
+int SoftBody::get_simulation_precision() {
+ return PhysicsServer::get_singleton()->soft_body_get_simulation_precision(physics_rid);
+}
+
+void SoftBody::set_simulation_precision(int p_simulation_precision) {
+ PhysicsServer::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision);
+}
+
+real_t SoftBody::get_total_mass() {
+ return PhysicsServer::get_singleton()->soft_body_get_total_mass(physics_rid);
+}
+
+void SoftBody::set_total_mass(real_t p_total_mass) {
+ PhysicsServer::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass);
+}
+
+void SoftBody::set_linear_stiffness(real_t p_linear_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness);
+}
+
+real_t SoftBody::get_linear_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_linear_stiffness(physics_rid);
+}
+
+void SoftBody::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness);
+}
+
+real_t SoftBody::get_areaAngular_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid);
+}
+
+void SoftBody::set_volume_stiffness(real_t p_volume_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness);
+}
+
+real_t SoftBody::get_volume_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_volume_stiffness(physics_rid);
+}
+
+real_t SoftBody::get_pressure_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_pressure_coefficient(physics_rid);
+}
+
+void SoftBody::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient);
+}
+
+real_t SoftBody::get_pose_matching_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid);
+}
+
+void SoftBody::set_pressure_coefficient(real_t p_pressure_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient);
+}
+
+real_t SoftBody::get_damping_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_damping_coefficient(physics_rid);
+}
+
+void SoftBody::set_damping_coefficient(real_t p_damping_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient);
+}
+
+real_t SoftBody::get_drag_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_drag_coefficient(physics_rid);
+}
+
+void SoftBody::set_drag_coefficient(real_t p_drag_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient);
+}
+
+Vector3 SoftBody::get_point_transform(int p_point_index) {
+ return PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index);
+}
+
+void SoftBody::pin_point_toggle(int p_point_index) {
+ pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index)));
+}
+
+void SoftBody::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) {
+ _pin_point_on_physics_server(p_point_index, pin);
+ if (pin) {
+ _add_pinned_point(p_point_index, p_spatial_attachment_path);
+ } else {
+ _remove_pinned_point(p_point_index);
+ }
+}
+
+bool SoftBody::is_point_pinned(int p_point_index) const {
+ return -1 != _has_pinned_point(p_point_index);
+}
+
+void SoftBody::set_ray_pickable(bool p_ray_pickable) {
+
+ ray_pickable = p_ray_pickable;
+ _update_pickable();
+}
+
+bool SoftBody::is_ray_pickable() const {
+
+ return ray_pickable;
+}
+
+SoftBody::SoftBody() :
+ MeshInstance(),
+ physics_rid(PhysicsServer::get_singleton()->soft_body_create()),
+ mesh_owner(false),
+ collision_mask(1),
+ collision_layer(1),
+ simulation_started(false),
+ pinned_points_cache_dirty(true) {
+
+ PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id());
+}
+
+SoftBody::~SoftBody() {
+}
+
+void SoftBody::reset_softbody_pin() {
+ PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid);
+ PoolVector<PinnedPoint>::Read pps = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 < i; --i) {
+ PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true);
+ }
+}
+
+void SoftBody::_update_cache_pin_points_datas() {
+ if (pinned_points_cache_dirty) {
+ pinned_points_cache_dirty = false;
+
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+
+ if (!w[i].spatial_attachment_path.is_empty()) {
+ w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path));
+ if (w[i].spatial_attachment) {
+
+ Transform point_global_transform(get_global_transform());
+ point_global_transform.translate(PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index));
+
+ // Local transform relative to spatial attachment node
+ w[i].vertex_offset_transform = w[i].spatial_attachment->get_global_transform().affine_inverse() * point_global_transform;
+ continue;
+ } else {
+ ERR_PRINTS("The node with path: " + String(w[i].spatial_attachment_path) + " was not found or is not a spatial node.");
+ }
+ }
+ // Local transform relative to Soft body
+ w[i].vertex_offset_transform.origin = PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index);
+ w[i].vertex_offset_transform.basis = Basis();
+ }
+ }
+}
+
+void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) {
+ PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin);
+}
+
+void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) {
+ SoftBody::PinnedPoint *pinned_point;
+ if (-1 == _get_pinned_point(p_point_index, pinned_point)) {
+ // Create new
+ PinnedPoint pp;
+ pp.point_index = p_point_index;
+ pp.spatial_attachment_path = p_spatial_attachment_path;
+ pinned_points_indices.push_back(pp);
+ } else {
+ // Update
+ pinned_point->point_index = p_point_index;
+ pinned_point->spatial_attachment_path = p_spatial_attachment_path;
+ }
+}
+
+void SoftBody::_remove_pinned_point(int p_point_index) {
+ const int id(_has_pinned_point(p_point_index));
+ if (-1 != id) {
+ pinned_points_indices.remove(id);
+ }
+}
+
+int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_point) const {
+ const int id = _has_pinned_point(p_point_index);
+ if (-1 == id) {
+ r_point = NULL;
+ return -1;
+ } else {
+ r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points_indices.read()[id]);
+ return id;
+ }
+}
+
+int SoftBody::_has_pinned_point(int p_point_index) const {
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+ if (p_point_index == r[i].point_index) {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h
new file mode 100644
index 0000000000..f44f337698
--- /dev/null
+++ b/scene/3d/soft_body.h
@@ -0,0 +1,197 @@
+/*************************************************************************/
+/* soft_physics_body.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 SOFT_PHYSICS_BODY_H
+#define SOFT_PHYSICS_BODY_H
+
+#include "scene/3d/mesh_instance.h"
+
+class SoftBody;
+
+class SoftBodyVisualServerHandler {
+
+ friend class SoftBody;
+
+ RID mesh;
+ int surface;
+ PoolVector<uint8_t> buffer;
+ uint32_t stride;
+ uint32_t offset_vertices;
+ uint32_t offset_normal;
+
+ PoolVector<uint8_t>::Write write_buffer;
+
+private:
+ SoftBodyVisualServerHandler();
+ bool is_ready() { return mesh.is_valid(); }
+ void prepare(RID p_mesh_rid, int p_surface);
+ void clear();
+ void open();
+ void close();
+ void commit_changes();
+
+public:
+ void set_vertex(int p_vertex_id, const void *p_vector3);
+ void set_normal(int p_vertex_id, const void *p_vector3);
+ void set_aabb(const AABB &p_aabb);
+};
+
+class SoftBody : public MeshInstance {
+ GDCLASS(SoftBody, MeshInstance);
+
+public:
+ struct PinnedPoint {
+ int point_index;
+ NodePath spatial_attachment_path;
+ Spatial *spatial_attachment; // Cache
+ /// This is the offset from the soft body to point or attachment to point
+ /// Depend if the spatial_attachment_node is NULL or not
+ Transform vertex_offset_transform; // Cache
+
+ PinnedPoint();
+ PinnedPoint(const PinnedPoint &obj_tocopy);
+ };
+
+private:
+ SoftBodyVisualServerHandler visual_server_handler;
+
+ RID physics_rid;
+
+ bool mesh_owner;
+ uint32_t collision_mask;
+ uint32_t collision_layer;
+ NodePath parent_collision_ignore;
+ PoolVector<PinnedPoint> pinned_points_indices;
+ bool simulation_started;
+ bool pinned_points_cache_dirty;
+
+ Ref<ArrayMesh> debug_mesh_cache;
+ class MeshInstance *debug_mesh;
+
+ bool capture_input_on_drag;
+ bool ray_pickable;
+
+ void _update_pickable();
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ bool _set_property_pinned_points_indices(const Array &p_indices);
+ bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value);
+ bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const;
+
+ virtual void _changed_callback(Object *p_changed, const char *p_prop);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ virtual String get_configuration_warning() const;
+
+protected:
+ void _draw_soft_mesh();
+
+public:
+ void update_physics_server();
+ void become_mesh_owner();
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_layer(uint32_t p_layer);
+ uint32_t get_collision_layer() const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
+ void set_collision_layer_bit(int p_bit, bool p_value);
+ bool get_collision_layer_bit(int p_bit) const;
+
+ void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore);
+ const NodePath &get_parent_collision_ignore() const;
+
+ void set_pinned_points_indices(PoolVector<PinnedPoint> p_pinned_points_indices);
+ PoolVector<PinnedPoint> get_pinned_points_indices();
+
+ void set_simulation_precision(int p_simulation_precision);
+ int get_simulation_precision();
+
+ void set_total_mass(real_t p_total_mass);
+ real_t get_total_mass();
+
+ void set_linear_stiffness(real_t p_linear_stiffness);
+ real_t get_linear_stiffness();
+
+ void set_areaAngular_stiffness(real_t p_areaAngular_stiffness);
+ real_t get_areaAngular_stiffness();
+
+ void set_volume_stiffness(real_t p_volume_stiffness);
+ real_t get_volume_stiffness();
+
+ void set_pressure_coefficient(real_t p_pressure_coefficient);
+ real_t get_pressure_coefficient();
+
+ void set_pose_matching_coefficient(real_t p_pose_matching_coefficient);
+ real_t get_pose_matching_coefficient();
+
+ void set_damping_coefficient(real_t p_damping_coefficient);
+ real_t get_damping_coefficient();
+
+ void set_drag_coefficient(real_t p_drag_coefficient);
+ real_t get_drag_coefficient();
+
+ void add_collision_exception_with(Node *p_node);
+ void remove_collision_exception_with(Node *p_node);
+
+ Vector3 get_point_transform(int p_point_index);
+
+ void pin_point_toggle(int p_point_index);
+ void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath());
+ bool is_point_pinned(int p_point_index) const;
+
+ void set_ray_pickable(bool p_ray_pickable);
+ bool is_ray_pickable() const;
+
+ SoftBody();
+ ~SoftBody();
+
+private:
+ void reset_softbody_pin();
+ void _update_cache_pin_points_datas();
+ void _pin_point_on_physics_server(int p_point_index, bool pin);
+ void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path);
+ void _remove_pinned_point(int p_point_index);
+ int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const;
+ int _has_pinned_point(int p_point_index) const;
+};
+
+#endif // SOFT_PHYSICS_BODY_H
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index 748aa8aad4..9b27faed6a 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -280,6 +280,10 @@ Transform Spatial::get_global_transform() const {
data.global_transform = data.local_transform;
}
+ if (data.disable_scale) {
+ data.global_transform.basis.orthonormalize();
+ }
+
data.dirty &= ~DIRTY_GLOBAL;
}
@@ -467,6 +471,15 @@ void Spatial::set_disable_gizmo(bool p_enabled) {
#endif
+void Spatial::set_disable_scale(bool p_enabled) {
+
+ data.disable_scale = p_enabled;
+}
+
+bool Spatial::is_scale_disabled() const {
+ return data.disable_scale;
+}
+
void Spatial::set_as_toplevel(bool p_enabled) {
if (data.toplevel == p_enabled)
@@ -735,6 +748,8 @@ void Spatial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Spatial::set_ignore_transform_notification);
ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &Spatial::set_as_toplevel);
ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel);
+ ClassDB::bind_method(D_METHOD("set_disable_scale", "disable"), &Spatial::set_disable_scale);
+ ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Spatial::is_scale_disabled);
ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world);
ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo);
@@ -755,15 +770,6 @@ void Spatial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &Spatial::set_notify_transform);
ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &Spatial::is_transform_notification_enabled);
- void rotate(const Vector3 &p_axis, float p_angle);
- void rotate_x(float p_angle);
- void rotate_y(float p_angle);
- void rotate_z(float p_angle);
- void translate(const Vector3 &p_offset);
- void scale(const Vector3 &p_ratio);
- void global_rotate(const Vector3 &p_axis, float p_angle);
- void global_translate(const Vector3 &p_offset);
-
ClassDB::bind_method(D_METHOD("rotate", "axis", "angle"), &Spatial::rotate);
ClassDB::bind_method(D_METHOD("global_rotate", "axis", "angle"), &Spatial::global_rotate);
ClassDB::bind_method(D_METHOD("global_scale", "scale"), &Spatial::global_scale);
@@ -797,6 +803,7 @@ void Spatial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
+
ADD_GROUP("Visibility", "");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo");
@@ -817,6 +824,7 @@ Spatial::Spatial() :
data.viewport = NULL;
data.inside_world = false;
data.visible = true;
+ data.disable_scale = false;
#ifdef TOOLS_ENABLED
data.gizmo_disabled = false;
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
index a43bed3e4a..653714dc98 100644
--- a/scene/3d/spatial.h
+++ b/scene/3d/spatial.h
@@ -92,6 +92,7 @@ class Spatial : public Node {
bool notify_transform;
bool visible;
+ bool disable_scale;
#ifdef TOOLS_ENABLED
Ref<SpatialGizmo> gizmo;
@@ -153,6 +154,9 @@ public:
void set_as_toplevel(bool p_enabled);
bool is_set_as_toplevel() const;
+ void set_disable_scale(bool p_enabled);
+ bool is_scale_disabled() const;
+
void set_disable_gizmo(bool p_enabled);
void update_gizmo();
void set_gizmo(const Ref<SpatialGizmo> &p_gizmo);
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index 385956dc16..4b870ca54c 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -942,8 +942,6 @@ VehicleBody::VehicleBody() :
engine_force = 0;
brake = 0;
- friction = 1;
-
state = NULL;
ccd = false;
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index 670df5cc7f..ba2807d4e8 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -113,7 +113,7 @@ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
if (min > rad || max < -rad) return false;
- /*======================== Z-tests ========================*/
+/*======================== Z-tests ========================*/
#define AXISTEST_Z12(a, b, fa, fb) \
p1 = a * v1.x - b * v1.y; \
@@ -1961,7 +1961,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh
#endif
for (int i = 0; i < height; i++) {
- //print_line("bake line " + itos(i) + " / " + itos(height));
+ //print_line("bake line " + itos(i) + " / " + itos(height));
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 1)
#endif
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 6993c0a785..1bc9fa4b12 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -1,5 +1,14 @@
#include "animation_blend_space_1d.h"
+void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) {
+
+ AnimationRootNode::set_tree(p_player);
+
+ for (int i = 0; i < blend_points_used; i++) {
+ blend_points[i].node->set_tree(p_player);
+ }
+}
+
void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const {
if (property.name.begins_with("blend_point_")) {
String left = property.name.get_slicec('/', 0);
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index 6e264076c4..d1ed4c6a1f 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -34,6 +34,8 @@ protected:
static void _bind_methods();
public:
+ virtual void set_tree(AnimationTree *p_player);
+
void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1);
void set_blend_point_position(int p_point, float p_position);
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 28e4ca4eda..bba25d64d9 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -1,6 +1,14 @@
#include "animation_blend_space_2d.h"
#include "math/delaunay.h"
+void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) {
+ AnimationRootNode::set_tree(p_player);
+
+ for (int i = 0; i < blend_points_used; i++) {
+ blend_points[i].node->set_tree(p_player);
+ }
+}
+
void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) {
ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
ERR_FAIL_COND(p_node.is_null());
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 5af0b9ba7f..74d20b6013 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -47,6 +47,8 @@ protected:
static void _bind_methods();
public:
+ virtual void set_tree(AnimationTree *p_player);
+
void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1);
void set_blend_point_position(int p_point, const Vector2 &p_position);
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index feacea5656..65904410d3 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -288,8 +288,8 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
- BIND_CONSTANT(MIX_MODE_BLEND)
- BIND_CONSTANT(MIX_MODE_ADD)
+ BIND_ENUM_CONSTANT(MIX_MODE_BLEND)
+ BIND_ENUM_CONSTANT(MIX_MODE_ADD)
}
AnimationNodeOneShot::AnimationNodeOneShot() {
@@ -311,33 +311,33 @@ AnimationNodeOneShot::AnimationNodeOneShot() {
////////////////////////////////////////////////
-void AnimationNodeAdd::set_amount(float p_amount) {
+void AnimationNodeAdd2::set_amount(float p_amount) {
amount = p_amount;
}
-float AnimationNodeAdd::get_amount() const {
+float AnimationNodeAdd2::get_amount() const {
return amount;
}
-String AnimationNodeAdd::get_caption() const {
- return "Add";
+String AnimationNodeAdd2::get_caption() const {
+ return "Add2";
}
-void AnimationNodeAdd::set_use_sync(bool p_sync) {
+void AnimationNodeAdd2::set_use_sync(bool p_sync) {
sync = p_sync;
}
-bool AnimationNodeAdd::is_using_sync() const {
+bool AnimationNodeAdd2::is_using_sync() const {
return sync;
}
-bool AnimationNodeAdd::has_filter() const {
+bool AnimationNodeAdd2::has_filter() const {
return true;
}
-float AnimationNodeAdd::process(float p_time, bool p_seek) {
+float AnimationNodeAdd2::process(float p_time, bool p_seek) {
float rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
@@ -345,19 +345,19 @@ float AnimationNodeAdd::process(float p_time, bool p_seek) {
return rem0;
}
-void AnimationNodeAdd::_bind_methods() {
+void AnimationNodeAdd2::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd::set_amount);
- ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd::get_amount);
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd2::set_amount);
+ ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd2::get_amount);
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd::is_using_sync);
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
}
-AnimationNodeAdd::AnimationNodeAdd() {
+AnimationNodeAdd2::AnimationNodeAdd2() {
add_input("in");
add_input("add");
@@ -365,6 +365,63 @@ AnimationNodeAdd::AnimationNodeAdd() {
sync = false;
}
+////////////////////////////////////////////////
+
+void AnimationNodeAdd3::set_amount(float p_amount) {
+ amount = p_amount;
+}
+
+float AnimationNodeAdd3::get_amount() const {
+ return amount;
+}
+
+String AnimationNodeAdd3::get_caption() const {
+ return "Add3";
+}
+void AnimationNodeAdd3::set_use_sync(bool p_sync) {
+
+ sync = p_sync;
+}
+
+bool AnimationNodeAdd3::is_using_sync() const {
+
+ return sync;
+}
+
+bool AnimationNodeAdd3::has_filter() const {
+
+ return true;
+}
+
+float AnimationNodeAdd3::process(float p_time, bool p_seek) {
+
+ blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync);
+ float rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync);
+
+ return rem0;
+}
+
+void AnimationNodeAdd3::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd3::set_amount);
+ ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd3::get_amount);
+
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
+}
+
+AnimationNodeAdd3::AnimationNodeAdd3() {
+
+ add_input("-add");
+ add_input("in");
+ add_input("+add");
+ amount = 0;
+ sync = false;
+}
/////////////////////////////////////////////
void AnimationNodeBlend2::set_amount(float p_amount) {
@@ -979,6 +1036,7 @@ bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_val
String name = p_name;
if (name.begins_with("nodes/")) {
+
String node_name = name.get_slicec('/', 1);
String what = name.get_slicec('/', 2);
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 8d7932196c..e86cc2e823 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -94,8 +94,8 @@ public:
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
-class AnimationNodeAdd : public AnimationNode {
- GDCLASS(AnimationNodeAdd, AnimationNode);
+class AnimationNodeAdd2 : public AnimationNode {
+ GDCLASS(AnimationNodeAdd2, AnimationNode);
float amount;
bool sync;
@@ -115,7 +115,31 @@ public:
virtual bool has_filter() const;
virtual float process(float p_time, bool p_seek);
- AnimationNodeAdd();
+ AnimationNodeAdd2();
+};
+
+class AnimationNodeAdd3 : public AnimationNode {
+ GDCLASS(AnimationNodeAdd3, AnimationNode);
+
+ float amount;
+ bool sync;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_amount(float p_amount);
+ float get_amount() const;
+
+ void set_use_sync(bool p_sync);
+ bool is_using_sync() const;
+
+ virtual bool has_filter() const;
+ virtual float process(float p_time, bool p_seek);
+
+ AnimationNodeAdd3();
};
class AnimationNodeBlend2 : public AnimationNode {
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index c5ad980806..36587a1e91 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -71,9 +71,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
- BIND_CONSTANT(SWITCH_MODE_IMMEDIATE);
- BIND_CONSTANT(SWITCH_MODE_SYNC);
- BIND_CONSTANT(SWITCH_MODE_AT_END);
+ BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE);
+ BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC);
+ BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END);
}
AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 06aaeddf18..111620dac1 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -419,14 +419,26 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
pa->capture = pa->object->get_indexed(pa->subpath);
}
- if (a->track_get_key_count(i) == 0)
+ int key_count = a->track_get_key_count(i);
+ if (key_count == 0)
continue; //eeh not worth it
float first_key_time = a->track_get_key_time(i, 0);
+ float transition = 1.0;
+ int first_key = 0;
+
+ if (first_key_time == 0.0) {
+ //ignore, use for transition
+ if (key_count == 1)
+ continue; //with one key we cant do anything
+ transition = a->track_get_key_transition(i, 0);
+ first_key_time = a->track_get_key_time(i, 1);
+ first_key = 1;
+ }
if (p_time < first_key_time) {
- float c = p_time / first_key_time;
- Variant first_value = a->track_get_key_value(i, 0);
+ float c = Math::ease(p_time / first_key_time, transition);
+ Variant first_value = a->track_get_key_value(i, first_key);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
@@ -537,6 +549,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
int s = params.size();
ERR_CONTINUE(s > VARIANT_ARG_MAX);
+#ifdef DEBUG_ENABLED
+ if (!nc->node->has_method(method)) {
+ ERR_PRINTS("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'.");
+ }
+#endif
+
if (can_call) {
MessageQueue::get_singleton()->push_call(
nc->node,
@@ -648,7 +666,22 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- if (nc->audio_start > p_time || (nc->audio_len > 0 && p_time - nc->audio_start < nc->audio_len)) {
+
+ bool loop = a->has_loop();
+
+ bool stop = false;
+
+ if (!loop && p_time < nc->audio_start) {
+ stop = true;
+ } else if (nc->audio_len > 0) {
+ float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
+
+ if (len > nc->audio_len) {
+ stop = true;
+ }
+ }
+
+ if (stop) {
//time to stop
nc->node->call("stop");
nc->audio_playing = false;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 7cc67b4cc3..4fa66e8ede 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -60,7 +60,7 @@ float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p
Ref<AnimationNodeBlendTree> tree = get_parent();
- if (!tree.is_valid() && get_tree()->get_graph_root().ptr() != this) {
+ if (!tree.is_valid() && get_tree()->get_tree_root().ptr() != this) {
make_invalid(RTR("Can't blend input because node is not in a tree"));
return 0;
}
@@ -225,6 +225,11 @@ void AnimationNode::set_input_connection(int p_input, const StringName &p_connec
}
String AnimationNode::get_caption() const {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("get_caption");
+ }
+
return "Node";
}
@@ -253,8 +258,15 @@ void AnimationNode::remove_input(int p_index) {
emit_changed();
}
+void AnimationNode::_set_parent(Object *p_parent) {
+ set_parent(Object::cast_to<AnimationNode>(p_parent));
+}
+
void AnimationNode::set_parent(AnimationNode *p_parent) {
parent = p_parent; //do not use ref because parent contains children
+ if (get_script_instance()) {
+ get_script_instance()->call("_parent_set", p_parent);
+ }
}
Ref<AnimationNode> AnimationNode::get_parent() const {
@@ -375,10 +387,17 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("blend_node", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("set_parent", "parent"), &AnimationNode::_set_parent);
+ ClassDB::bind_method(D_METHOD("get_parent"), &AnimationNode::get_parent);
+ ClassDB::bind_method(D_METHOD("get_tree"), &AnimationNode::get_tree);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters");
BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek")));
+ BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption"));
+ BIND_VMETHOD(MethodInfo(Variant::STRING, "has_filter"));
+ BIND_VMETHOD(MethodInfo("_parent_set", PropertyInfo(Variant::OBJECT, "parent")));
ADD_SIGNAL(MethodInfo("removed_from_graph"));
BIND_ENUM_CONSTANT(FILTER_IGNORE);
@@ -398,7 +417,7 @@ AnimationNode::AnimationNode() {
////////////////////
-void AnimationTree::set_graph_root(const Ref<AnimationNode> &p_root) {
+void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
if (root.is_valid()) {
root->set_tree(NULL);
@@ -416,7 +435,7 @@ void AnimationTree::set_graph_root(const Ref<AnimationNode> &p_root) {
update_configuration_warning();
}
-Ref<AnimationNode> AnimationTree::get_graph_root() const {
+Ref<AnimationNode> AnimationTree::get_tree_root() const {
return root;
}
@@ -681,6 +700,7 @@ void AnimationTree::_clear_caches() {
void AnimationTree::_process_graph(float p_delta) {
//check all tracks, see if they need modification
+ root_motion_transform = Transform();
if (!root.is_valid()) {
ERR_PRINT("AnimationTree: root AnimationNode is not set, disabling playback.");
@@ -770,6 +790,8 @@ void AnimationTree::_process_graph(float p_delta) {
continue; //may happen should not
}
+ track->root_motion = root_motion_track == path;
+
ERR_CONTINUE(!state.track_map.has(path));
int blend_idx = state.track_map[path];
@@ -786,29 +808,85 @@ void AnimationTree::_process_graph(float p_delta) {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- Vector3 loc;
- Quat rot;
- Vector3 scale;
-
- 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 (err != OK)
- continue;
-
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3();
t->rot = Quat();
+ t->rot_blend_accum = 0;
t->scale = Vector3();
}
- t->loc = t->loc.linear_interpolate(loc, blend);
- t->rot = t->rot.slerp(rot, blend);
- t->scale = t->scale.linear_interpolate(scale, blend);
+ if (track->root_motion) {
+
+ float prev_time = time - delta;
+ if (prev_time < 0) {
+ if (!a->has_loop()) {
+ prev_time = 0;
+ } else {
+ prev_time = a->get_length() + prev_time;
+ }
+ }
+
+ Vector3 loc[2];
+ Quat rot[2];
+ Vector3 scale[2];
+
+ if (prev_time > time) {
+
+ Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+ t->scale += (scale[1] - scale[0]) * blend;
+ Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+
+ prev_time = 0;
+ }
+
+ Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+ t->scale += (scale[1] - scale[0]) * blend;
+ Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+
+ prev_time = 0;
+
+ } else {
+ Vector3 loc;
+ Quat rot;
+ Vector3 scale;
+
+ 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 (err != OK)
+ continue;
+
+ t->loc = t->loc.linear_interpolate(loc, blend);
+ if (t->rot_blend_accum == 0) {
+ t->rot = rot;
+ t->rot_blend_accum = blend;
+ } else {
+ float rot_total = t->rot_blend_accum + blend;
+ t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
+ t->rot_blend_accum = rot_total;
+ }
+ t->scale = t->scale.linear_interpolate(scale, blend);
+ }
} break;
case Animation::TYPE_VALUE: {
@@ -963,7 +1041,22 @@ void AnimationTree::_process_graph(float p_delta) {
t->start = time;
}
} else if (t->playing) {
- if (t->start > time || (t->len > 0 && time - t->start < t->len)) {
+
+ bool loop = a->has_loop();
+
+ bool stop = false;
+
+ if (!loop && time < t->start) {
+ stop = true;
+ } else if (t->len > 0) {
+ float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
+
+ if (len > t->len) {
+ stop = true;
+ }
+ }
+
+ if (stop) {
//time to stop
t->object->call("stop");
t->playing = false;
@@ -972,6 +1065,12 @@ void AnimationTree::_process_graph(float p_delta) {
}
}
+ float db = Math::linear2db(MAX(blend, 0.00001));
+ if (t->object->has_method("set_unit_db")) {
+ t->object->call("set_unit_db", db);
+ } else {
+ t->object->call("set_volume_db", db);
+ }
} break;
case Animation::TYPE_ANIMATION: {
@@ -1059,11 +1158,18 @@ 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
+ 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->skeleton && t->bone_idx >= 0) {
+ if (t->root_motion) {
+
+ root_motion_transform = xform;
+
+ if (t->skeleton && t->bone_idx >= 0) {
+ root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
+ }
+ } else if (t->skeleton && t->bone_idx >= 0) {
t->skeleton->set_bone_pose(t->bone_idx, xform);
@@ -1174,12 +1280,24 @@ String AnimationTree::get_configuration_warning() const {
return warning;
}
+void AnimationTree::set_root_motion_track(const NodePath &p_track) {
+ root_motion_track = p_track;
+}
+
+NodePath AnimationTree::get_root_motion_track() const {
+ return root_motion_track;
+}
+
+Transform AnimationTree::get_root_motion_transform() const {
+ return root_motion_transform;
+}
+
void AnimationTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_active", "active"), &AnimationTree::set_active);
ClassDB::bind_method(D_METHOD("is_active"), &AnimationTree::is_active);
- ClassDB::bind_method(D_METHOD("set_graph_root", "root"), &AnimationTree::set_graph_root);
- ClassDB::bind_method(D_METHOD("get_graph_root"), &AnimationTree::get_graph_root);
+ ClassDB::bind_method(D_METHOD("set_tree_root", "root"), &AnimationTree::set_tree_root);
+ ClassDB::bind_method(D_METHOD("get_tree_root"), &AnimationTree::get_tree_root);
ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &AnimationTree::set_process_mode);
ClassDB::bind_method(D_METHOD("get_process_mode"), &AnimationTree::get_process_mode);
@@ -1187,12 +1305,22 @@ void AnimationTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation_player", "root"), &AnimationTree::set_animation_player);
ClassDB::bind_method(D_METHOD("get_animation_player"), &AnimationTree::get_animation_player);
+ ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track);
+ ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track);
+
+ ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform);
+
ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "graph_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_graph_root", "get_graph_root");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player"), "set_animation_player", "get_animation_player");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode");
+ ADD_GROUP("Root Motion", "root_motion_");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track");
+
+ BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
+ BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
}
AnimationTree::AnimationTree() {
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index adc25d5607..540c36437a 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -82,6 +82,8 @@ protected:
void _validate_property(PropertyInfo &property) const;
+ void _set_parent(Object *p_parent);
+
public:
void set_parent(AnimationNode *p_parent);
Ref<AnimationNode> get_parent() const;
@@ -135,6 +137,8 @@ public:
private:
struct TrackCache {
+
+ bool root_motion;
uint64_t setup_pass;
uint64_t process_pass;
Animation::TrackType type;
@@ -142,6 +146,7 @@ private:
ObjectID object_id;
TrackCache() {
+ root_motion = false;
setup_pass = 0;
process_pass = 0;
object = NULL;
@@ -156,6 +161,7 @@ private:
int bone_idx;
Vector3 loc;
Quat rot;
+ float rot_blend_accum;
Vector3 scale;
TrackCacheTransform() {
@@ -235,13 +241,16 @@ private:
bool started;
+ NodePath root_motion_track;
+ Transform root_motion_transform;
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void set_graph_root(const Ref<AnimationNode> &p_root);
- Ref<AnimationNode> get_graph_root() const;
+ void set_tree_root(const Ref<AnimationNode> &p_root);
+ Ref<AnimationNode> get_tree_root() const;
void set_active(bool p_active);
bool is_active() const;
@@ -257,6 +266,11 @@ public:
bool is_state_invalid() const;
String get_invalid_state_reason() const;
+ void set_root_motion_track(const NodePath &p_track);
+ NodePath get_root_motion_track() const;
+
+ Transform get_root_motion_transform() const;
+
uint64_t get_last_process_pass() const;
AnimationTree();
~AnimationTree();
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 143684bdf9..026215508b 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -1814,7 +1814,7 @@ void AnimationTreePlayer::_bind_methods() {
ADD_GROUP("Playback", "playback_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_animation_process_mode", "get_animation_process_mode");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "master_player"), "set_master_player", "get_master_player");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "master_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_master_player", "get_master_player");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "base_path"), "set_base_path", "get_base_path");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp
new file mode 100644
index 0000000000..a28c63064a
--- /dev/null
+++ b/scene/animation/root_motion_view.cpp
@@ -0,0 +1,178 @@
+#include "root_motion_view.h"
+#include "scene/animation/animation_tree.h"
+#include "scene/resources/material.h"
+void RootMotionView::set_animation_path(const NodePath &p_path) {
+ path = p_path;
+ first = true;
+}
+
+NodePath RootMotionView::get_animation_path() const {
+ return path;
+}
+
+void RootMotionView::set_color(const Color &p_color) {
+ color = p_color;
+ first = true;
+}
+
+Color RootMotionView::get_color() const {
+ return color;
+}
+
+void RootMotionView::set_cell_size(float p_size) {
+ cell_size = p_size;
+ first = true;
+}
+
+float RootMotionView::get_cell_size() const {
+ return cell_size;
+}
+
+void RootMotionView::set_radius(float p_radius) {
+ radius = p_radius;
+ first = true;
+}
+
+float RootMotionView::get_radius() const {
+ return radius;
+}
+
+void RootMotionView::set_zero_y(bool p_zero_y) {
+ zero_y = p_zero_y;
+}
+
+bool RootMotionView::get_zero_y() const {
+ return zero_y;
+}
+
+void RootMotionView::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+
+ VS::get_singleton()->immediate_set_material(immediate, SpatialMaterial::get_material_rid_for_2d(false, true, false, false, false));
+ first = true;
+ }
+
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
+ Transform transform;
+
+ if (has_node(path)) {
+
+ Node *node = get_node(path);
+
+ AnimationTree *tree = Object::cast_to<AnimationTree>(node);
+ if (tree && tree->is_active() && tree->get_root_motion_track() != NodePath()) {
+ if (is_processing_internal() && tree->get_process_mode() == AnimationTree::ANIMATION_PROCESS_PHYSICS) {
+ set_process_internal(false);
+ set_physics_process_internal(true);
+ }
+
+ if (is_physics_processing_internal() && tree->get_process_mode() == AnimationTree::ANIMATION_PROCESS_IDLE) {
+ set_process_internal(true);
+ set_physics_process_internal(false);
+ }
+
+ transform = tree->get_root_motion_transform();
+ }
+ }
+
+ if (!first && transform == Transform()) {
+ return;
+ }
+
+ first = false;
+
+ transform.orthonormalize(); //dont want scale, too imprecise
+ transform.affine_invert();
+
+ accumulated = transform * accumulated;
+ accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
+ if (zero_y) {
+ accumulated.origin.y = 0;
+ }
+ accumulated.origin.z = Math::fposmod(accumulated.origin.z, cell_size);
+
+ VS::get_singleton()->immediate_clear(immediate);
+
+ int cells_in_radius = int((radius / cell_size) + 1.0);
+
+ VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_LINES);
+ for (int i = -cells_in_radius; i < cells_in_radius; i++) {
+ for (int j = -cells_in_radius; j < cells_in_radius; j++) {
+
+ Vector3 from(i * cell_size, 0, j * cell_size);
+ Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
+ Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
+ from = accumulated.xform(from);
+ from_i = accumulated.xform(from_i);
+ from_j = accumulated.xform(from_j);
+
+ Color c = color, c_i = color, c_j = color;
+ c.a *= MAX(0, 1.0 - from.length() / radius);
+ c_i.a *= MAX(0, 1.0 - from_i.length() / radius);
+ c_j.a *= MAX(0, 1.0 - from_j.length() / radius);
+
+ VS::get_singleton()->immediate_color(immediate, c);
+ VS::get_singleton()->immediate_vertex(immediate, from);
+
+ VS::get_singleton()->immediate_color(immediate, c_i);
+ VS::get_singleton()->immediate_vertex(immediate, from_i);
+
+ VS::get_singleton()->immediate_color(immediate, c);
+ VS::get_singleton()->immediate_vertex(immediate, from);
+
+ VS::get_singleton()->immediate_color(immediate, c_j);
+ VS::get_singleton()->immediate_vertex(immediate, from_j);
+ }
+ }
+
+ VS::get_singleton()->immediate_end(immediate);
+ }
+}
+
+AABB RootMotionView::get_aabb() const {
+
+ return AABB(Vector3(-radius, 0, -radius), Vector3(radius * 2, 0.001, radius * 2));
+}
+PoolVector<Face3> RootMotionView::get_faces(uint32_t p_usage_flags) const {
+ return PoolVector<Face3>();
+}
+
+void RootMotionView::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_animation_path", "path"), &RootMotionView::set_animation_path);
+ ClassDB::bind_method(D_METHOD("get_animation_path"), &RootMotionView::get_animation_path);
+
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &RootMotionView::set_color);
+ ClassDB::bind_method(D_METHOD("get_color"), &RootMotionView::get_color);
+
+ ClassDB::bind_method(D_METHOD("set_cell_size", "size"), &RootMotionView::set_cell_size);
+ ClassDB::bind_method(D_METHOD("get_cell_size"), &RootMotionView::get_cell_size);
+
+ ClassDB::bind_method(D_METHOD("set_radius", "size"), &RootMotionView::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &RootMotionView::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_zero_y", "enable"), &RootMotionView::set_zero_y);
+ ClassDB::bind_method(D_METHOD("get_zero_y"), &RootMotionView::get_zero_y);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationTree"), "set_animation_path", "get_animation_path");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_y"), "set_zero_y", "get_zero_y");
+}
+
+RootMotionView::RootMotionView() {
+ zero_y = true;
+ radius = 10;
+ cell_size = 1;
+ set_process_internal(true);
+ immediate = VisualServer::get_singleton()->immediate_create();
+ set_base(immediate);
+ color = Color(0.5, 0.5, 1.0);
+}
+
+RootMotionView::~RootMotionView() {
+ set_base(RID());
+ VisualServer::get_singleton()->free(immediate);
+}
diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h
new file mode 100644
index 0000000000..611183d364
--- /dev/null
+++ b/scene/animation/root_motion_view.h
@@ -0,0 +1,47 @@
+#ifndef ROOT_MOTION_VIEW_H
+#define ROOT_MOTION_VIEW_H
+
+#include "scene/3d/visual_instance.h"
+
+class RootMotionView : public VisualInstance {
+ GDCLASS(RootMotionView, VisualInstance)
+public:
+ RID immediate;
+ NodePath path;
+ float cell_size;
+ float radius;
+ bool use_in_game;
+ Color color;
+ bool first;
+ bool zero_y;
+
+ Transform accumulated;
+
+private:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_animation_path(const NodePath &p_path);
+ NodePath get_animation_path() const;
+
+ void set_color(const Color &p_path);
+ Color get_color() const;
+
+ void set_cell_size(float p_size);
+ float get_cell_size() const;
+
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ void set_zero_y(bool p_zero_y);
+ bool get_zero_y() const;
+
+ virtual AABB get_aabb() const;
+ virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ RootMotionView();
+ ~RootMotionView();
+};
+
+#endif // ROOT_MOTION_VIEW_H
diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp
index 408c00334a..863b278b62 100644
--- a/scene/audio/audio_player.cpp
+++ b/scene/audio/audio_player.cpp
@@ -41,10 +41,10 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
int buffer_size = mix_buffer.size();
if (p_fadeout) {
- buffer_size = MIN(buffer_size, 16); //short fadeout ramp
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
}
- //mix
stream_playback->mix(buffer, pitch_scale, buffer_size);
//multiply volume interpolating to avoid clicks if this changes
@@ -56,6 +56,7 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
buffer[i] *= vol;
vol += vol_inc;
}
+
//set volume for next mix
mix_volume_db = target_volume;
@@ -90,11 +91,14 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
void AudioStreamPlayer::_mix_audio() {
- if (!stream_playback.is_valid()) {
+ if (!stream_playback.is_valid() || !active)
return;
- }
- if (!active) {
+ if (stream_paused) {
+ if (stream_paused_fade) {
+ _mix_internal(true);
+ stream_paused_fade = false;
+ }
return;
}
@@ -135,6 +139,17 @@ void AudioStreamPlayer::_notification(int p_what) {
AudioServer::get_singleton()->remove_callback(_mix_audios, this);
}
+
+ if (p_what == NOTIFICATION_PAUSED) {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ }
+
+ if (p_what == NOTIFICATION_UNPAUSED) {
+ set_stream_paused(false);
+ }
}
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
@@ -159,7 +174,6 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
- ERR_FAIL_COND(stream_playback.is_null());
}
}
@@ -275,6 +289,19 @@ bool AudioStreamPlayer::_is_active() const {
return active;
}
+void AudioStreamPlayer::set_stream_paused(bool p_pause) {
+
+ if (p_pause != stream_paused) {
+ stream_paused = p_pause;
+ stream_paused_fade = p_pause ? true : false;
+ }
+}
+
+bool AudioStreamPlayer::get_stream_paused() const {
+
+ return stream_paused;
+}
+
void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
if (property.name == "bus") {
@@ -328,11 +355,15 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer::_bus_layout_changed);
+ 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);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
@@ -351,6 +382,8 @@ AudioStreamPlayer::AudioStreamPlayer() {
autoplay = false;
setseek = -1;
active = false;
+ stream_paused = false;
+ stream_paused_fade = false;
mix_target = MIX_TARGET_STEREO;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
diff --git a/scene/audio/audio_player.h b/scene/audio/audio_player.h
index 21189aea6d..591c00ed18 100644
--- a/scene/audio/audio_player.h
+++ b/scene/audio/audio_player.h
@@ -57,6 +57,8 @@ private:
float pitch_scale;
float volume_db;
bool autoplay;
+ bool stream_paused;
+ bool stream_paused_fade;
StringName bus;
MixTarget mix_target;
@@ -100,6 +102,9 @@ public:
void set_mix_target(MixTarget p_target);
MixTarget get_mix_target() const;
+ void set_stream_paused(bool p_pause);
+ bool get_stream_paused() const;
+
AudioStreamPlayer();
~AudioStreamPlayer();
};
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 34891832e2..8e232c6f46 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -242,6 +242,14 @@ bool ColorPicker::is_raw_mode() const {
return raw_mode_enabled;
}
+void ColorPicker::set_deferred_mode(bool p_enabled) {
+ deferred_mode_enabled = p_enabled;
+}
+
+bool ColorPicker::is_deferred_mode() const {
+ return deferred_mode_enabled;
+}
+
void ColorPicker::_update_text_value() {
bool visible = true;
if (text_is_constructor) {
@@ -328,7 +336,11 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
+ } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
emit_signal("color_changed", color);
+ changing_color = false;
} else {
changing_color = false;
}
@@ -347,7 +359,8 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
}
}
@@ -368,7 +381,10 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
+ else if (!bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT)
+ emit_signal("color_changed", color);
}
Ref<InputEventMouseMotion> mev = p_event;
@@ -383,7 +399,8 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
}
}
@@ -500,6 +517,8 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
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("add_preset", "color"), &ColorPicker::add_preset);
@@ -522,6 +541,7 @@ 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, "raw_mode"), "set_raw_mode", "is_raw_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
}
@@ -533,6 +553,7 @@ ColorPicker::ColorPicker() :
edit_alpha = true;
text_is_constructor = false;
raw_mode_enabled = false;
+ deferred_mode_enabled = false;
changing_color = false;
screen = NULL;
@@ -722,8 +743,9 @@ ColorPicker *ColorPickerButton::get_picker() {
return picker;
}
-PopupPanel *ColorPickerButton::get_popup() const {
+PopupPanel *ColorPickerButton::get_popup() {
+ _update_picker();
return popup;
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 6b63e5fe60..0166da7118 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -67,6 +67,7 @@ private:
Color color;
bool raw_mode_enabled;
+ bool deferred_mode_enabled;
bool updating;
bool changing_color;
float h, s, v;
@@ -107,6 +108,9 @@ public:
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_focus_on_line_edit();
ColorPicker();
@@ -140,7 +144,7 @@ public:
bool is_editing_alpha() const;
ColorPicker *get_picker();
- PopupPanel *get_popup() const;
+ PopupPanel *get_popup();
ColorPickerButton();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index a738687a70..17c349858f 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -160,9 +160,16 @@ void Control::_update_minimum_size_cache() {
Size2 minsize = get_minimum_size();
minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
+
+ bool size_changed = false;
+ if (data.minimum_size_cache != minsize)
+ size_changed = true;
+
data.minimum_size_cache = minsize;
data.minimum_size_valid = true;
- minimum_size_changed();
+
+ if (size_changed)
+ minimum_size_changed();
}
Size2 Control::get_combined_minimum_size() const {
@@ -1267,35 +1274,34 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_type) c
return Theme::get_default()->has_constant(p_name, type);
}
-Size2 Control::get_parent_area_size() const {
-
- ERR_FAIL_COND_V(!is_inside_tree(), Size2());
-
- Size2 parent_size;
+Rect2 Control::get_parent_anchorable_rect() const {
+ if (!is_inside_tree())
+ return Rect2();
+ Rect2 parent_rect;
if (data.parent_canvas_item) {
-
- parent_size = data.parent_canvas_item->_edit_get_rect().size;
+ parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
-
- parent_size = get_viewport()->get_visible_rect().size;
+ parent_rect = get_viewport()->get_visible_rect();
}
- return parent_size;
+ return parent_rect;
}
-void Control::_size_changed() {
+Size2 Control::get_parent_area_size() const {
- if (!is_inside_tree())
- return;
+ return get_parent_anchorable_rect().size;
+}
+
+void Control::_size_changed() {
- Size2 parent_size = get_parent_area_size();
+ Rect2 parent_rect = get_parent_anchorable_rect();
float margin_pos[4];
for (int i = 0; i < 4; i++) {
- float area = parent_size[i & 1];
+ float area = parent_rect.size[i & 1];
margin_pos[i] = data.margin[i] + (data.anchor[i] * area);
}
@@ -1325,9 +1331,9 @@ void Control::_size_changed() {
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
- if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
- new_size_cache = new_size_cache.floor();
- new_pos_cache = new_pos_cache.floor();
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ new_size_cache = new_size_cache.round();
+ new_pos_cache = new_pos_cache.round();
}
bool pos_changed = new_pos_cache != data.pos_cache;
bool size_changed = new_size_cache != data.size_cache;
@@ -1335,57 +1341,25 @@ void Control::_size_changed() {
data.pos_cache = new_pos_cache;
data.size_cache = new_size_cache;
- if (size_changed) {
- notification(NOTIFICATION_RESIZED);
- }
- if (pos_changed || size_changed) {
- item_rect_changed(size_changed);
- _change_notify_margins();
- _notify_transform();
- }
+ if (is_inside_tree()) {
+ if (size_changed) {
+ notification(NOTIFICATION_RESIZED);
+ }
+ if (pos_changed || size_changed) {
+ item_rect_changed(size_changed);
+ _change_notify_margins();
+ _notify_transform();
+ }
- if (pos_changed && !size_changed) {
- _update_canvas_item_transform(); //move because it won't be updated
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
}
}
-float Control::_get_parent_range(int p_idx) const {
-
- if (!is_inside_tree()) {
-
- return 0;
- }
- if (data.parent_canvas_item) {
-
- return data.parent_canvas_item->_edit_get_rect().size[p_idx & 1];
- } else {
- return get_viewport()->get_visible_rect().size[p_idx & 1];
- }
-
- return 0;
-}
-
-float Control::_get_range(int p_idx) const {
-
- p_idx &= 1;
-
- float parent_range = _get_parent_range(p_idx);
- float from = _a2s(data.margin[p_idx], data.anchor[p_idx], parent_range);
- float to = _a2s(data.margin[p_idx + 2], data.anchor[p_idx + 2], parent_range);
-
- return to - from;
-}
-
-float Control::_s2a(float p_val, float p_anchor, float p_range) const {
- return p_val - (p_anchor * p_range);
-}
-
-float Control::_a2s(float p_val, float p_anchor, float p_range) const {
- return Math::floor(p_val + (p_anchor * p_range));
-}
-
void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bool p_push_opposite_anchor) {
- float parent_range = _get_parent_range((p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? 0 : 1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
+ float parent_range = (p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
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;
@@ -1401,9 +1375,9 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
if (!p_keep_margin) {
- data.margin[p_margin] = _s2a(previous_margin_pos, data.anchor[p_margin], parent_range);
+ data.margin[p_margin] = previous_margin_pos - data.anchor[p_margin] * parent_range;
if (p_push_opposite_anchor) {
- data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range);
+ data.margin[(p_margin + 2) % 4] = previous_opposite_margin_pos - data.anchor[(p_margin + 2) % 4] * parent_range;
}
}
if (is_inside_tree()) {
@@ -1411,7 +1385,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
update();
- _change_notify();
+ _change_notify("anchor");
}
void Control::_set_anchor(Margin p_margin, float p_anchor) {
@@ -1557,8 +1531,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
new_size.y = min_size.y;
}
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
//Left
switch (p_preset) {
@@ -1570,21 +1543,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[0] = pw * (0.0 - data.anchor[0]) + p_margin;
+ data.margin[0] = parent_rect.size.x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[0] = pw * (0.5 - data.anchor[0]) - new_size.x / 2;
+ data.margin[0] = parent_rect.size.x * (0.5 - data.anchor[0]) - new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
- data.margin[0] = pw * (1.0 - data.anchor[0]) - new_size.x - p_margin;
+ data.margin[0] = parent_rect.size.x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x;
break;
}
@@ -1598,21 +1571,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[1] = ph * (0.0 - data.anchor[1]) + p_margin;
+ data.margin[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[1] = ph * (0.5 - data.anchor[1]) - new_size.y / 2;
+ data.margin[1] = parent_rect.size.y * (0.5 - data.anchor[1]) - new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
- data.margin[1] = ph * (1.0 - data.anchor[1]) - new_size.y - p_margin;
+ data.margin[1] = parent_rect.size.y * (1.0 - data.anchor[1]) - new_size.y - p_margin + parent_rect.position.y;
break;
}
@@ -1622,14 +1595,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
- data.margin[2] = pw * (0.0 - data.anchor[2]) + new_size.x + p_margin;
+ data.margin[2] = parent_rect.size.x * (0.0 - data.anchor[2]) + new_size.x + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[2] = pw * (0.5 - data.anchor[2]) + new_size.x / 2;
+ data.margin[2] = parent_rect.size.x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
@@ -1640,7 +1613,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[2] = pw * (1.0 - data.anchor[2]) - p_margin;
+ data.margin[2] = parent_rect.size.x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x;
break;
}
@@ -1650,14 +1623,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
- data.margin[3] = ph * (0.0 - data.anchor[3]) + new_size.y + p_margin;
+ data.margin[3] = parent_rect.size.y * (0.0 - data.anchor[3]) + new_size.y + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[3] = ph * (0.5 - data.anchor[3]) + new_size.y / 2;
+ data.margin[3] = parent_rect.size.y * (0.5 - data.anchor[3]) + new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
@@ -1668,7 +1641,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[3] = ph * (1.0 - data.anchor[3]) - p_margin;
+ data.margin[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y;
break;
}
@@ -1747,31 +1720,29 @@ void Control::set_global_position(const Point2 &p_point) {
set_position(inv.xform(p_point));
}
-void Control::set_position(const Size2 &p_point) {
-
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
- float x2 = _a2s(data.margin[2], data.anchor[2], pw);
- float y2 = _a2s(data.margin[3], data.anchor[3], ph);
+ Rect2 anchorable = get_parent_anchorable_rect();
+ Rect2 result = anchorable;
+ for (int i = 0; i < 4; i++) {
+ result.grow_margin((Margin)i, p_anchors[i] * anchorable.get_size()[i % 2] + p_margins[i]);
+ }
- Size2 ret = Size2(x2 - x, y2 - y);
- Size2 min = get_combined_minimum_size();
+ return result;
+}
- Size2 size = Size2(MAX(min.width, ret.width), MAX(min.height, ret.height));
- float w = size.x;
- float h = size.y;
+void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
- x = p_point.x;
- y = p_point.y;
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ r_margins[0] = Math::floor(p_rect.position.x - (p_anchors[0] * parent_rect_size.x));
+ r_margins[1] = Math::floor(p_rect.position.y - (p_anchors[1] * parent_rect_size.y));
+ r_margins[2] = Math::floor(p_rect.position.x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x));
+ r_margins[3] = Math::floor(p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y));
+}
- data.margin[0] = _s2a(x, data.anchor[0], pw);
- data.margin[1] = _s2a(y, data.anchor[1], ph);
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
+void Control::set_position(const Size2 &p_point) {
+ _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
_size_changed();
}
@@ -1784,18 +1755,7 @@ void Control::set_size(const Size2 &p_size) {
if (new_size.y < min.y)
new_size.y = min.y;
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
-
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
-
- float w = new_size.width;
- float h = new_size.height;
-
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
-
+ _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
_size_changed();
}
@@ -1826,6 +1786,11 @@ Rect2 Control::get_rect() const {
return Rect2(get_position(), get_size());
}
+Rect2 Control::get_anchorable_rect() const {
+
+ return Rect2(Point2(), get_size());
+}
+
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
ERR_FAIL_COND(p_icon.is_null());
@@ -2223,10 +2188,17 @@ void Control::set_tooltip(const String &p_tooltip) {
data.tooltip = p_tooltip;
}
+
String Control::get_tooltip(const Point2 &p_pos) const {
return data.tooltip;
}
+Control *Control::make_custom_tooltip(const String &p_text) const {
+ if (get_script_instance()) {
+ return const_cast<Control *>(this)->call("_make_custom_tooltip", p_text);
+ }
+ return NULL;
+}
void Control::set_default_cursor_shape(CursorShape p_shape) {
@@ -2325,12 +2297,11 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
Point2 points[4];
Transform2D xform = get_global_transform();
- Rect2 rect = _edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
const Vector2 dir[4] = {
Vector2(-1, 0),
@@ -2384,12 +2355,11 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con
Point2 points[4];
Transform2D xform = c->get_global_transform();
- Rect2 rect = c->_edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
float min = 1e7;
@@ -2857,6 +2827,7 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::OBJECT, "get_drag_data", PropertyInfo(Variant::VECTOR2, "position")));
BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
+ BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text")));
ADD_GROUP("Anchor", "anchor_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT);
@@ -2888,12 +2859,12 @@ void Control::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
ADD_GROUP("Focus", "focus_");
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next"), "set_focus_next", "get_focus_next");
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous"), "set_focus_previous", "get_focus_previous");
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
+ ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_GROUP("Mouse", "mouse_");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 9124256624..94231867d7 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -202,12 +202,12 @@ private:
NodePath focus_next;
NodePath focus_prev;
- HashMap<StringName, Ref<Texture>, StringNameHasher> icon_override;
- HashMap<StringName, Ref<Shader>, StringNameHasher> shader_override;
- HashMap<StringName, Ref<StyleBox>, StringNameHasher> style_override;
- HashMap<StringName, Ref<Font>, StringNameHasher> font_override;
- HashMap<StringName, Color, StringNameHasher> color_override;
- HashMap<StringName, int, StringNameHasher> constant_override;
+ HashMap<StringName, Ref<Texture> > icon_override;
+ HashMap<StringName, Ref<Shader> > shader_override;
+ HashMap<StringName, Ref<StyleBox> > style_override;
+ HashMap<StringName, Ref<Font> > font_override;
+ HashMap<StringName, Color> color_override;
+ HashMap<StringName, int> constant_override;
Map<Ref<Font>, int> font_refcount;
} data;
@@ -220,10 +220,6 @@ private:
void _set_anchor(Margin p_margin, float p_anchor);
- float _get_parent_range(int p_idx) const;
- float _get_range(int p_idx) const;
- float _s2a(float p_val, float p_anchor, float p_range) const;
- float _a2s(float p_val, float p_anchor, float p_range) const;
void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
void _theme_changed();
@@ -233,6 +229,9 @@ private:
void _update_scroll();
void _resize(const Size2 &p_size);
+ 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 _size_changed();
String _get_tooltip() const;
@@ -283,6 +282,7 @@ public:
};
+ /* EDITOR */
virtual Dictionary _edit_get_state() const;
virtual void _edit_set_state(const Dictionary &p_state);
@@ -358,6 +358,7 @@ public:
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
+ Rect2 get_anchorable_rect() const;
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
@@ -453,6 +454,7 @@ public:
void set_tooltip(const String &p_tooltip);
virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual Control *make_custom_tooltip(const String &p_text) const;
/* CURSOR */
@@ -465,6 +467,7 @@ public:
bool is_toplevel_control() const;
Size2 get_parent_area_size() const;
+ Rect2 get_parent_anchorable_rect() const;
void grab_click_focus();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 57b9a9a11b..aa52739b0a 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -285,7 +285,7 @@ void ItemList::unselect_all() {
items[i].selected = false;
}
-
+ current = -1;
update();
}
@@ -942,6 +942,7 @@ void ItemList::_notification(int p_what) {
}
}
+ minimum_size_changed();
shape_changed = false;
}
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 9af479c1cc..0b36e1663c 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -75,7 +75,7 @@ void Label::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
- if (clip || autowrap) {
+ if (clip) {
VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 0cd5219f8f..b71a4dd133 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -711,7 +711,8 @@ void LineEdit::_notification(int p_what) {
if (selected)
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color);
- drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
+ int yofs = y_ofs + (caret_height - font->get_height()) / 2;
+ drawer.draw_char(ci, Point2(x_ofs, yofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
if (char_ofs == cursor_pos && draw_caret) {
if (ime_text.length() == 0) {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index c5e4149782..2901176a69 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -36,7 +36,7 @@ Size2 OptionButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
if (has_icon("arrow"))
- minsize.width += Control::get_icon("arrow")->get_width();
+ minsize.width += Control::get_icon("arrow")->get_width() + get_constant("hseparation");
return minsize;
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 18e609c798..ebec61ee6d 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -398,6 +398,15 @@ void PopupMenu::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
+ if (pm) {
+ // Inherit submenu's popup delay time from parent menu
+ float pm_delay = pm->get_submenu_popup_delay();
+ set_submenu_popup_delay(pm_delay);
+ }
+ } break;
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < items.size(); i++) {
@@ -421,6 +430,8 @@ void PopupMenu::_notification(int p_what) {
Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
+ Ref<StyleBox> labeled_separator_left = get_stylebox("labeled_separator_left");
+ Ref<StyleBox> labeled_separator_right = get_stylebox("labeled_separator_right");
style->draw(ci, Rect2(Point2(), get_size()));
Point2 ofs = style->get_offset();
@@ -457,10 +468,25 @@ void PopupMenu::_notification(int p_what) {
hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));
}
+ String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
+
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ if (text != String()) {
+ int ss = font->get_string_size(text).width;
+ int center = (get_size().width) / 2;
+ int l = center - ss / 2;
+ int r = center + ss / 2;
+ if (l > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h)));
+ }
+ if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) {
+ labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h)));
+ }
+ } else {
+ separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ }
}
if (items[i].checkable_type) {
@@ -480,8 +506,13 @@ void PopupMenu::_notification(int p_what) {
}
item_ofs.y += font->get_ascent();
- String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
- if (!items[i].separator) {
+ if (items[i].separator) {
+
+ if (text != String()) {
+ int center = (get_size().width - font->get_string_size(text).width) / 2;
+ font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled);
+ }
+ } else {
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));
}
@@ -1064,11 +1095,15 @@ void PopupMenu::remove_item(int p_idx) {
update();
}
-void PopupMenu::add_separator() {
+void PopupMenu::add_separator(const String &p_text) {
Item sep;
sep.separator = true;
sep.ID = -1;
+ if (p_text != String()) {
+ sep.text = p_text;
+ sep.xl_text = tr(p_text);
+ }
items.push_back(sep);
update();
}
@@ -1201,6 +1236,19 @@ bool PopupMenu::is_hide_on_multistate_item_selection() const {
return hide_on_multistate_item_selection;
}
+void PopupMenu::set_submenu_popup_delay(float p_time) {
+
+ if (p_time <= 0)
+ p_time = 0.01;
+
+ submenu_timer->set_wait_time(p_time);
+}
+
+float PopupMenu::get_submenu_popup_delay() const {
+
+ return submenu_timer->get_wait_time();
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1288,7 +1336,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
- ClassDB::bind_method(D_METHOD("add_separator"), &PopupMenu::add_separator);
+ ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
@@ -1303,12 +1351,15 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_state_item_selection", "enable"), &PopupMenu::set_hide_on_multistate_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_state_item_selection"), &PopupMenu::is_hide_on_multistate_item_selection);
+ ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay);
+ ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay);
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");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index d3ee9be1c0..8ec51c7d3a 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -180,7 +180,7 @@ public:
void remove_item(int p_idx);
- void add_separator();
+ void add_separator(const String &p_text = String());
void clear();
@@ -202,6 +202,9 @@ public:
void set_hide_on_multistate_item_selection(bool p_enabled);
bool is_hide_on_multistate_item_selection() const;
+ void set_submenu_popup_delay(float p_time);
+ float get_submenu_popup_delay() const;
+
virtual void popup(const Rect2 &p_bounds = Rect2());
PopupMenu();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f34559fc8d..857ae8ff4c 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -324,7 +324,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
color = _find_color(text, p_base_color);
font_color_shadow = _find_color(text, p_font_color_shadow);
underline = _find_underline(text);
- if (_find_meta(text, &meta)) {
+ if (_find_meta(text, &meta) && underline_meta) {
underline = true;
}
@@ -1253,6 +1253,9 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
//validate invalid lines
Size2 size = get_size();
+ if (fixed_width != -1) {
+ size.width = fixed_width;
+ }
Rect2 text_rect = _get_text_rect();
Color font_color_shadow = get_color("font_color_shadow");
bool use_outline = get_constant("shadow_as_outline");
@@ -2245,6 +2248,21 @@ int RichTextLabel::get_total_character_count() const {
return tc;
}
+void RichTextLabel::set_fixed_size_to_width(int p_width) {
+ fixed_width = p_width;
+ minimum_size_changed();
+}
+
+Size2 RichTextLabel::get_minimum_size() const {
+
+ if (fixed_width != -1) {
+ const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
+ return Size2(fixed_width, const_cast<RichTextLabel *>(this)->get_content_height());
+ }
+
+ return Size2();
+}
+
RichTextLabel::RichTextLabel() {
main = memnew(ItemFrame);
@@ -2287,6 +2305,7 @@ RichTextLabel::RichTextLabel() {
percent_visible = 1;
visible_line_count = 0;
+ fixed_width = -1;
set_clip_contents(true);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index af368af46a..06e9b8efe3 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -293,6 +293,8 @@ private:
void _update_all_lines();
+ int fixed_width;
+
protected:
void _notification(int p_what);
@@ -368,6 +370,9 @@ public:
void set_percent_visible(float p_percent);
float get_percent_visible() const;
+ void set_fixed_size_to_width(int p_width);
+ virtual Size2 get_minimum_size() const;
+
RichTextLabel();
~RichTextLabel();
};
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 6ec67aca6b..e5bd1c453d 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -257,9 +257,7 @@ void ScrollBar::_notification(int p_what) {
Point2 ofs;
- VisualServer *vs = VisualServer::get_singleton();
-
- vs->canvas_item_add_texture_rect(ci, Rect2(Point2(), decr->get_size()), decr->get_rid());
+ decr->draw(ci, Point2());
if (orientation == HORIZONTAL)
ofs.x += decr->get_width();
@@ -280,7 +278,7 @@ void ScrollBar::_notification(int p_what) {
else
ofs.height += area.height;
- vs->canvas_item_add_texture_rect(ci, Rect2(ofs, decr->get_size()), incr->get_rid());
+ incr->draw(ci, ofs);
Rect2 grabber_rect;
if (orientation == HORIZONTAL) {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 46215c9277..b820e2eafd 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -65,11 +65,12 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
} else {
grab.active = false;
}
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
-
- set_value(get_value() + get_step());
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
- set_value(get_value() - get_step());
+ } else if (scrollable) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ set_value(get_value() + get_step());
+ } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ set_value(get_value() - get_step());
+ }
}
}
@@ -247,6 +248,16 @@ bool Slider::is_editable() const {
return editable;
}
+void Slider::set_scrollable(bool p_scrollable) {
+
+ scrollable = p_scrollable;
+}
+
+bool Slider::is_scrollable() const {
+
+ return scrollable;
+}
+
void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Slider::_gui_input);
@@ -258,8 +269,11 @@ void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_editable", "editable"), &Slider::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &Slider::is_editable);
+ ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable);
+ ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ticks_on_borders"), "set_ticks_on_borders", "get_ticks_on_borders");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
@@ -272,5 +286,6 @@ Slider::Slider(Orientation p_orientation) {
ticks = 0;
custom_step = -1;
editable = true;
+ scrollable = true;
set_focus_mode(FOCUS_ALL);
}
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index e77a0b7423..4d02348159 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -48,6 +48,7 @@ class Slider : public Range {
Orientation orientation;
float custom_step;
bool editable;
+ bool scrollable;
protected:
void _gui_input(Ref<InputEvent> p_event);
@@ -70,6 +71,9 @@ public:
void set_editable(bool p_editable);
bool is_editable() const;
+ void set_scrollable(bool p_scrollable);
+ bool is_scrollable() const;
+
Slider(Orientation p_orientation = VERTICAL);
};
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 4f72b5c6ed..c30fa96327 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -407,7 +407,7 @@ void TabContainer::_child_renamed_callback() {
void TabContainer::add_child_notify(Node *p_child) {
- Control::add_child_notify(p_child);
+ Container::add_child_notify(p_child);
Control *c = Object::cast_to<Control>(p_child);
if (!c)
@@ -515,7 +515,7 @@ Control *TabContainer::get_current_tab_control() const {
void TabContainer::remove_child_notify(Node *p_child) {
- Control::remove_child_notify(p_child);
+ Container::remove_child_notify(p_child);
call_deferred("_update_current_tab");
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 1afe5f7541..8a3c9d2bb2 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -31,11 +31,11 @@
#ifndef TAB_CONTAINER_H
#define TAB_CONTAINER_H
-#include "scene/gui/control.h"
+#include "scene/gui/container.h"
#include "scene/gui/popup.h"
-class TabContainer : public Control {
+class TabContainer : public Container {
- GDCLASS(TabContainer, Control);
+ GDCLASS(TabContainer, Container);
public:
enum TabAlign {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 215ba0271f..cccd1bd197 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -290,6 +290,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
Line line;
line.marked = false;
+ line.safe = false;
line.breakpoint = false;
line.hidden = false;
line.width_cache = -1;
@@ -336,10 +337,6 @@ void TextEdit::_update_scrollbars() {
int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1;
int visible_rows = get_visible_rows();
- int first_vis_line = get_first_visible_line();
- int wi;
- int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi));
-
int total_rows = get_total_visible_rows();
if (scroll_past_end_of_file_enabled) {
total_rows += visible_rows - 1;
@@ -426,6 +423,9 @@ void TextEdit::_update_scrollbars() {
void TextEdit::_click_selection_held() {
+ // Warning: is_mouse_button_pressed(BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
+ // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem.
+ // I'm unsure if there's an actual fix that doesn't have a ton of side effects.
if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != Selection::MODE_NONE) {
switch (selection.selecting_mode) {
case Selection::MODE_POINTER: {
@@ -455,7 +455,7 @@ void TextEdit::_update_selection_mode_pointer() {
select(selection.selecting_line, selection.selecting_column, row, col);
cursor_set_line(row, false);
- cursor_set_column(col, false);
+ cursor_set_column(col);
update();
click_select_held->start();
@@ -496,21 +496,24 @@ void TextEdit::_update_selection_mode_word() {
selection.selected_word_beg = beg;
selection.selected_word_end = end;
selection.selected_word_origin = beg;
+ cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column);
} else {
if ((col <= selection.selected_word_origin && row == selection.selecting_line) || row < selection.selecting_line) {
selection.selecting_column = selection.selected_word_end;
select(row, beg, selection.selecting_line, selection.selected_word_end);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
} else {
selection.selecting_column = selection.selected_word_beg;
select(selection.selecting_line, selection.selected_word_beg, row, end);
+ cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column);
}
}
- cursor_set_line(row, false);
update();
+
click_select_held->start();
}
@@ -531,7 +534,7 @@ void TextEdit::_update_selection_mode_line() {
selection.selecting_column = 0;
col = text[row].length();
}
- cursor_set_column(0, false);
+ cursor_set_column(0);
select(selection.selecting_line, selection.selecting_column, row, col);
update();
@@ -960,12 +963,13 @@ void TextEdit::_notification(int p_what) {
// draw line numbers
if (cache.line_number_w) {
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
String fc = String::num(line + 1);
while (fc.length() < line_number_char_count) {
fc = line_num_padding + fc;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
+ 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);
}
}
@@ -1088,12 +1092,13 @@ void TextEdit::_notification(int p_what) {
}
if (brace_matching_enabled) {
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
if ((brace_open_match_line == line && brace_open_match_column == last_wrap_column + j) ||
(cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) {
if (brace_open_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) ||
@@ -1101,7 +1106,7 @@ void TextEdit::_notification(int p_what) {
if (brace_close_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
@@ -1161,9 +1166,10 @@ void TextEdit::_notification(int p_what) {
}
if (str[j] >= 32) {
- int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
+ int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
if (underlined) {
- draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
} else if (draw_tabs && str[j] == '\t') {
int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
@@ -1528,8 +1534,11 @@ void TextEdit::backspace_at_cursor() {
if (is_line_hidden(cursor.line))
set_line_as_hidden(prev_line, true);
- if (is_line_set_as_breakpoint(cursor.line))
+ if (is_line_set_as_breakpoint(cursor.line)) {
+ if (!text.is_breakpoint(prev_line))
+ emit_signal("breakpoint_toggled", prev_line);
set_line_as_breakpoint(prev_line, true);
+ }
if (auto_brace_completion_enabled &&
cursor.column > 0 &&
@@ -1665,9 +1674,11 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
if (is_wrap_enabled() || is_hiding_enabled()) {
- int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + 1, wrap_index) - 1;
- row = first_vis_line + f_ofs;
- row = CLAMP(row, 0, get_last_visible_line() + 1);
+ int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1;
+ if (rows < 0)
+ row = first_vis_line - f_ofs;
+ else
+ row = first_vis_line + f_ofs;
}
if (row < 0)
@@ -3301,22 +3312,37 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
ERR_FAIL_INDEX(p_line, text.size());
ERR_FAIL_COND(p_char < 0);
- /* STEP 1 add spaces if the char is greater than the end of the line */
+ /* STEP 1 remove \r from source text and separate in substrings */
+
+ Vector<String> substrings = p_text.replace("\r", "").split("\n");
+
+ /* STEP 2 fire breakpoint_toggled signals */
+
+ // Is this just a new empty line?
+ bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
+
+ int i = p_line + !shift_first_line;
+ int lines = substrings.size() - 1;
+ for (; i < text.size(); i++) {
+ if (text.is_breakpoint(i)) {
+ if ((i - lines < p_line || !text.is_breakpoint(i - lines)) || (i - lines == p_line && !shift_first_line))
+ emit_signal("breakpoint_toggled", i);
+ if (i + lines >= text.size() || !text.is_breakpoint(i + lines))
+ emit_signal("breakpoint_toggled", i + lines);
+ }
+ }
+
+ /* STEP 3 add spaces if the char is greater than the end of the line */
while (p_char > text[p_line].length()) {
text.set(p_line, text[p_line] + String::chr(' '));
}
- /* STEP 2 separate dest string in pre and post text */
+ /* STEP 4 separate dest string in pre and post text */
String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
- /* STEP 3 remove \r from source text and separate in substrings */
-
- //buh bye \r and split
- Vector<String> substrings = p_text.replace("\r", "").split("\n");
-
for (int i = 0; i < substrings.size(); i++) {
//insert the substrings
@@ -3334,9 +3360,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
}
- // if we are just making a new empty line, reset breakpoints and hidden status
- if (p_char == 0 && p_text.replace("\r", "") == "\n") {
-
+ 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_breakpoint(p_line, false);
@@ -3392,11 +3416,20 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- for (int i = p_from_line; i < p_to_line; i++) {
+ int lines = p_to_line - p_from_line;
- text.remove(p_from_line + 1);
+ for (int i = p_from_line + 1; i < text.size(); i++) {
+ if (text.is_breakpoint(i)) {
+ if (i + lines >= text.size() || !text.is_breakpoint(i + lines))
+ emit_signal("breakpoint_toggled", i);
+ if (i > p_to_line && (i - lines < 0 || !text.is_breakpoint(i - lines)))
+ emit_signal("breakpoint_toggled", i - lines);
+ }
}
+ for (int i = p_from_line; i < p_to_line; i++) {
+ text.remove(p_from_line + 1);
+ }
text.set(p_from_line, pre_text + post_text);
text.set_line_wrap_amount(p_from_line, -1);
@@ -3762,7 +3795,6 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
}
// line ends before hit wrap_at; add this word to the substring
wrap_substring += word_str;
- px += word_px;
lines.push_back(wrap_substring);
return lines;
}
@@ -4277,6 +4309,7 @@ void TextEdit::_update_caches() {
cache.caret_color = get_color("caret_color");
cache.caret_background_color = get_color("caret_background_color");
cache.line_number_color = get_color("line_number_color");
+ cache.safe_line_number_color = get_color("safe_line_number_color");
cache.font_color = get_color("font_color");
cache.font_selected_color = get_color("font_selected_color");
cache.keyword_color = get_color("keyword_color");
@@ -4848,6 +4881,17 @@ void TextEdit::set_line_as_marked(int p_line, bool p_marked) {
update();
}
+void TextEdit::set_line_as_safe(int p_line, bool p_safe) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ text.set_safe(p_line, p_safe);
+ update();
+}
+
+bool TextEdit::is_line_set_as_safe(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ return text.is_safe(p_line);
+}
+
bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);
@@ -4869,6 +4913,24 @@ void TextEdit::get_breakpoints(List<int> *p_breakpoints) const {
}
}
+Array TextEdit::get_breakpoints_array() const {
+
+ Array arr;
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_breakpoint(i))
+ arr.append(i);
+ }
+ return arr;
+}
+
+void TextEdit::remove_breakpoints() {
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_breakpoint(i))
+ /* Should "breakpoint_toggled" be fired when breakpoints are removed this way? */
+ text.set_breakpoint(i, false);
+ }
+}
+
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -5451,9 +5513,8 @@ int TextEdit::get_last_visible_line() const {
int TextEdit::get_last_visible_line_wrap_index() const {
int first_vis_line = get_first_visible_line();
- int last_vis_line = 0;
int wi;
- last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi);
return wi;
}
@@ -5538,7 +5599,17 @@ void TextEdit::_confirm_completion() {
cursor_set_column(cursor.column - completion_base.length(), false);
insert_text_at_cursor(completion_current);
- if (completion_current.ends_with("(") && auto_brace_completion_enabled) {
+ // When inserted into the middle of an existing string, don't add an unnecessary quote
+ 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) {
+ _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--;
}
@@ -5765,18 +5836,23 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
+ char selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
- if (s[i] == '"') {
- if (inside_quotes) {
- qend = i;
- inside_quotes = false;
- if (col >= qbegin && col <= qend) {
- return s.substr(qbegin, qend - qbegin);
+ if (s[i] == '"' || s[i] == '\'') {
+ if (i == 0 || s[i - 1] != '\\') {
+ if (inside_quotes && selected_quote == s[i]) {
+ qend = i;
+ inside_quotes = false;
+ selected_quote = '\0';
+ if (col >= qbegin && col <= qend) {
+ return s.substr(qbegin, qend - qbegin);
+ }
+ } else if (!inside_quotes) {
+ qbegin = i + 1;
+ inside_quotes = true;
+ selected_quote = s[i];
}
- } else {
- qbegin = i + 1;
- inside_quotes = true;
}
}
}
@@ -5857,12 +5933,12 @@ void TextEdit::set_line_length_guideline_column(int p_column) {
update();
}
-void TextEdit::set_draw_breakpoint_gutter(bool p_draw) {
+void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
draw_breakpoint_gutter = p_draw;
update();
}
-bool TextEdit::is_drawing_breakpoint_gutter() const {
+bool TextEdit::is_breakpoint_gutter_enabled() const {
return draw_breakpoint_gutter;
}
@@ -6045,6 +6121,8 @@ 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_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_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -6083,11 +6161,15 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
+ ClassDB::bind_method(D_METHOD("get_breakpoints"), &TextEdit::get_breakpoints_array);
+ ClassDB::bind_method(D_METHOD("remove_breakpoints"), &TextEdit::remove_breakpoints);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
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, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
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");
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 5c82d1ac20..34d69bb508 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -76,6 +76,7 @@ public:
bool marked : 1;
bool breakpoint : 1;
bool hidden : 1;
+ bool safe : 1;
int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
String data;
@@ -106,6 +107,8 @@ public:
bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
+ void set_safe(int p_line, bool p_safe) { text[p_line].safe = p_safe; }
+ bool is_safe(int p_line) const { return text[p_line].safe; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
@@ -165,6 +168,7 @@ private:
Color caret_color;
Color caret_background_color;
Color line_number_color;
+ Color safe_line_number_color;
Color font_color;
Color font_selected_color;
Color keyword_color;
@@ -472,7 +476,11 @@ public:
void set_line_as_marked(int p_line, bool p_marked);
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(int p_line) const;
+ 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_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
@@ -632,8 +640,8 @@ public:
void set_show_line_length_guideline(bool p_show);
void set_line_length_guideline_column(int p_column);
- void set_draw_breakpoint_gutter(bool p_draw);
- bool is_drawing_breakpoint_gutter() const;
+ void set_breakpoint_gutter_enabled(bool p_draw);
+ bool is_breakpoint_gutter_enabled() const;
void set_breakpoint_gutter_width(int p_gutter_width);
int get_breakpoint_gutter_width() const;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index ae21775c55..4750e05633 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -30,8 +30,6 @@
#include "http_request.h"
-#include "version.h"
-
void HTTPRequest::_redirect_request(const String &p_new_url) {
}
@@ -106,28 +104,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h
validate_ssl = p_ssl_validate_domain;
- bool has_user_agent = false;
- bool has_accept = false;
headers = p_custom_headers;
request_data = p_request_data;
- for (int i = 0; i < headers.size(); i++) {
-
- if (headers[i].findn("user-agent:") == 0)
- has_user_agent = true;
- if (headers[i].findn("Accept:") == 0)
- has_accept = true;
- }
-
- if (!has_user_agent) {
- headers.push_back("User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")");
- }
-
- if (!has_accept) {
- headers.push_back("Accept: */*");
- }
-
requesting = true;
if (use_threads) {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 4dc7b03685..b7b26d1c55 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -240,7 +240,7 @@ void Node::_propagate_enter_tree() {
void Node::_propagate_exit_tree() {
-//block while removing children
+ //block while removing children
#ifdef DEBUG_ENABLED
@@ -809,6 +809,22 @@ bool Node::is_processing_internal() const {
return data.idle_process_internal;
}
+void Node::set_process_priority(int p_priority) {
+ data.process_priority = p_priority;
+
+ if (is_processing())
+ data.tree->make_group_changed("idle_process");
+
+ if (is_processing_internal())
+ data.tree->make_group_changed("idle_process_internal");
+
+ if (is_physics_processing())
+ data.tree->make_group_changed("physics_process");
+
+ if (is_physics_processing_internal())
+ data.tree->make_group_changed("physics_process_internal");
+}
+
void Node::set_process_input(bool p_enable) {
if (p_enable == data.input)
@@ -1388,6 +1404,11 @@ bool Node::is_greater_than(const Node *p_node) const {
return res;
}
+bool Node::has_priority_higher_than(const Node *p_node) const {
+ ERR_FAIL_NULL_V(p_node, false);
+ return data.process_priority > p_node->data.process_priority;
+}
+
void Node::get_owned_by(Node *p_by, List<Node *> *p_owned) {
if (data.owner == p_by)
@@ -1895,7 +1916,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
// Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later
// but remember non-instanced nodes that are hidden below instanced ones
if (descendant->data.owner != this) {
- if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this)
+ if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this && descendant->data.owner != descendant->get_parent())
hidden_roots.push_back(descendant);
continue;
}
@@ -1934,8 +1955,9 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) {
Resource *res = Object::cast_to<Resource>(value);
- if (res) // Duplicate only if it's a resource
+ if (res) { // Duplicate only if it's a resource
current_node->set(name, res->duplicate());
+ }
} else {
@@ -2607,6 +2629,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_physics_processing"), &Node::is_physics_processing);
ClassDB::bind_method(D_METHOD("get_process_delta_time"), &Node::get_process_delta_time);
ClassDB::bind_method(D_METHOD("set_process", "enable"), &Node::set_process);
+ ClassDB::bind_method(D_METHOD("set_process_priority", "priority"), &Node::set_process_priority);
ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing);
ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input);
ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input);
@@ -2758,6 +2781,7 @@ Node::Node() {
data.tree = NULL;
data.physics_process = false;
data.idle_process = false;
+ data.process_priority = 0;
data.physics_process_internal = false;
data.idle_process_internal = false;
data.inside_tree = false;
diff --git a/scene/main/node.h b/scene/main/node.h
index 341349de79..4b8f584ba7 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -70,6 +70,11 @@ public:
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
+ struct ComparatorWithPriority {
+
+ bool operator()(const Node *p_a, const Node *p_b) const { return p_b->has_priority_higher_than(p_a) || p_b->is_greater_than(p_a); }
+ };
+
private:
struct GroupData {
@@ -118,6 +123,7 @@ private:
//should move all the stuff below to bits
bool physics_process;
bool idle_process;
+ int process_priority;
bool physics_process_internal;
bool idle_process_internal;
@@ -259,6 +265,7 @@ public:
bool is_a_parent_of(const Node *p_node) const;
bool is_greater_than(const Node *p_node) const;
+ bool has_priority_higher_than(const Node *p_node) const;
NodePath get_path() const;
NodePath get_path_to(const Node *p_node) const;
@@ -319,6 +326,8 @@ public:
void set_process_internal(bool p_idle_process_internal);
bool is_processing_internal() const;
+ void set_process_priority(int p_priority);
+
void set_process_input(bool p_enable);
bool is_processing_input() const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 8d6e57b335..3424c4edac 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -132,6 +132,12 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
group_map.erase(E);
}
+void SceneTree::make_group_changed(const StringName &p_group) {
+ Map<StringName, Group>::Element *E = group_map.find(p_group);
+ if (E)
+ E->get().changed = true;
+}
+
void SceneTree::flush_transform_notifications() {
SelfList<Node> *n = xform_change_list.first();
@@ -165,7 +171,7 @@ void SceneTree::_flush_ugc() {
ugc_locked = false;
}
-void SceneTree::_update_group_order(Group &g) {
+void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
if (!g.changed)
return;
@@ -175,8 +181,13 @@ void SceneTree::_update_group_order(Group &g) {
Node **nodes = &g.nodes[0];
int node_count = g.nodes.size();
- SortArray<Node *, Node::Comparator> node_sort;
- node_sort.sort(nodes, node_count);
+ if (p_use_priority) {
+ SortArray<Node *, Node::ComparatorWithPriority> node_sort;
+ node_sort.sort(nodes, node_count);
+ } else {
+ SortArray<Node *, Node::Comparator> node_sort;
+ node_sort.sort(nodes, node_count);
+ }
g.changed = false;
}
@@ -498,7 +509,8 @@ bool SceneTree::idle(float p_time) {
_notify_group_pause("idle_process_internal", Node::NOTIFICATION_INTERNAL_PROCESS);
_notify_group_pause("idle_process", Node::NOTIFICATION_PROCESS);
- Size2 win_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ Size2 win_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
+
if (win_size != last_screen_size) {
last_screen_size = win_size;
@@ -656,6 +668,11 @@ void SceneTree::_notification(int p_notification) {
#endif
} break;
+ case NOTIFICATION_CRASH: {
+
+ get_root()->propagate_notification(p_notification);
+ } break;
+
default:
break;
};
@@ -915,7 +932,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
if (g.nodes.empty())
return;
- _update_group_order(g);
+ _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
//copy, so copy on write happens in case something is removed from process while being called
//performance is not lost because only if something is added/removed the vector is copied.
@@ -1112,7 +1129,7 @@ void SceneTree::_update_root_rect() {
}
//actual screen video mode
- Size2 video_mode = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ Size2 video_mode = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
Size2 desired_res = stretch_min;
Size2 viewport_size;
@@ -2004,7 +2021,7 @@ SceneTree::SceneTree() {
stretch_aspect = STRETCH_ASPECT_IGNORE;
stretch_shrink = 1;
- last_screen_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ last_screen_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
_update_root_rect();
if (ScriptDebugger::get_singleton()) {
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index aa8d78b1e1..11201097d4 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -161,7 +161,7 @@ private:
bool ugc_locked;
void _flush_ugc();
- _FORCE_INLINE_ void _update_group_order(Group &g);
+ _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false);
void _update_listener();
Array _get_nodes_in_group(const StringName &p_group);
@@ -204,6 +204,7 @@ private:
Group *add_to_group(const StringName &p_group, Node *p_node);
void remove_from_group(const StringName &p_group, Node *p_node);
+ void make_group_changed(const StringName &p_group);
void _notify_group_pause(const StringName &p_group, int p_notification);
void _call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 1236aea2dd..9013d276c7 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -41,7 +41,7 @@
#include "scene/3d/spatial.h"
#include "scene/gui/control.h"
#include "scene/gui/label.h"
-#include "scene/gui/panel.h"
+#include "scene/gui/panel_container.h"
#include "scene/main/timer.h"
#include "scene/resources/mesh.h"
#include "scene/scene_string_names.h"
@@ -144,7 +144,7 @@ void ViewportTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path"), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Viewport"), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
}
ViewportTexture::ViewportTexture() {
@@ -166,9 +166,9 @@ ViewportTexture::~ViewportTexture() {
/////////////////////////////////////
-class TooltipPanel : public Panel {
+class TooltipPanel : public PanelContainer {
- GDCLASS(TooltipPanel, Panel)
+ GDCLASS(TooltipPanel, PanelContainer)
public:
TooltipPanel(){};
};
@@ -626,7 +626,7 @@ Rect2 Viewport::get_visible_rect() const {
if (size == Size2()) {
- r = Rect2(Point2(), Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height));
+ r = Rect2(Point2(), Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height));
} else {
r = Rect2(Point2(), size);
@@ -1305,10 +1305,11 @@ void Viewport::_gui_cancel_tooltip() {
if (gui.tooltip_popup) {
gui.tooltip_popup->queue_delete();
gui.tooltip_popup = NULL;
+ gui.tooltip_label = NULL;
}
}
-String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) {
+String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which) {
Vector2 pos = p_pos;
String tooltip;
@@ -1317,6 +1318,10 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) {
tooltip = p_control->get_tooltip(pos);
+ if (r_which) {
+ *r_which = p_control;
+ }
+
if (tooltip != String())
break;
pos = p_control->get_transform().xform(pos);
@@ -1338,41 +1343,49 @@ void Viewport::_gui_show_tooltip() {
return;
}
- String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos));
+ Control *which = NULL;
+ String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos), &which);
if (tooltip.length() == 0)
return; // bye
if (gui.tooltip_popup) {
memdelete(gui.tooltip_popup);
gui.tooltip_popup = NULL;
+ gui.tooltip_label = NULL;
}
- if (!gui.tooltip) {
+ if (!which) {
return;
}
- Control *rp = gui.tooltip->get_root_parent_control();
+ Control *rp = which; //->get_root_parent_control();
if (!rp)
return;
- gui.tooltip_popup = memnew(TooltipPanel);
+ gui.tooltip_popup = which->make_custom_tooltip(tooltip);
+
+ if (!gui.tooltip_popup) {
+ gui.tooltip_popup = memnew(TooltipPanel);
+
+ gui.tooltip_label = memnew(TooltipLabel);
+ gui.tooltip_popup->add_child(gui.tooltip_label);
+
+ Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel");
+
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT));
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP));
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT));
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
+ gui.tooltip_label->set_text(tooltip.strip_edges());
+ }
rp->add_child(gui.tooltip_popup);
gui.tooltip_popup->force_parent_owned();
- gui.tooltip_label = memnew(TooltipLabel);
- gui.tooltip_popup->add_child(gui.tooltip_label);
gui.tooltip_popup->set_as_toplevel(true);
- gui.tooltip_popup->hide();
-
- Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel");
+ //gui.tooltip_popup->hide();
- gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT));
- gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP));
- gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT));
- gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
- gui.tooltip_label->set_text(tooltip.strip_edges());
- Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_minimum_size() + ttp->get_minimum_size());
- Rect2 vr = gui.tooltip_label->get_viewport_rect();
+ Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_popup->get_minimum_size());
+ Rect2 vr = gui.tooltip_popup->get_viewport_rect();
if (r.size.x + r.position.x > vr.size.x)
r.position.x = vr.size.x - r.size.x;
else if (r.position.x < 0)
@@ -1404,6 +1417,8 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
mb->get_button_index() == BUTTON_WHEEL_UP ||
mb->get_button_index() == BUTTON_WHEEL_LEFT ||
mb->get_button_index() == BUTTON_WHEEL_RIGHT));
+ Ref<InputEventPanGesture> pn = p_input;
+ cant_stop_me_now = pn.is_valid() || cant_stop_me_now;
bool ismouse = ev.is_valid() || Object::cast_to<InputEventMouseMotion>(*p_input) != NULL;
@@ -1889,13 +1904,18 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
bool is_tooltip_shown = false;
if (gui.tooltip_popup) {
- if (can_tooltip) {
+ if (can_tooltip && gui.tooltip) {
String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos));
if (tooltip.length() == 0)
_gui_cancel_tooltip();
- else if (tooltip == gui.tooltip_label->get_text())
+ else if (gui.tooltip_label) {
+ if (tooltip == gui.tooltip_label->get_text()) {
+ is_tooltip_shown = true;
+ }
+ } else if (tooltip == String(gui.tooltip_popup->call("get_tooltip_text"))) {
is_tooltip_shown = true;
+ }
} else
_gui_cancel_tooltip();
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 3000398540..e717d27069 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -255,7 +255,7 @@ private:
Control *key_focus;
Control *mouse_over;
Control *tooltip;
- Panel *tooltip_popup;
+ Control *tooltip_popup;
Label *tooltip_label;
Point2 tooltip_pos;
Point2 last_mouse_pos;
@@ -312,7 +312,7 @@ private:
void _gui_remove_root_control(List<Control *>::Element *RI);
void _gui_remove_subwindow_control(List<Control *>::Element *SI);
- String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos);
+ String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which = NULL);
void _gui_cancel_tooltip();
void _gui_show_tooltip();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 333e5c8e08..0d69c037fe 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -51,6 +51,7 @@
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
#include "scene/2d/particles_2d.h"
+
#include "scene/2d/path_2d.h"
#include "scene/2d/physics_body_2d.h"
#include "scene/2d/polygon_2d.h"
@@ -66,10 +67,11 @@
#include "scene/animation/animation_blend_space_1d.h"
#include "scene/animation/animation_blend_space_2d.h"
#include "scene/animation/animation_blend_tree.h"
-#include "scene/animation/animation_tree.h"
#include "scene/animation/animation_node_state_machine.h"
#include "scene/animation/animation_player.h"
+#include "scene/animation/animation_tree.h"
#include "scene/animation/animation_tree_player.h"
+#include "scene/animation/root_motion_view.h"
#include "scene/animation/tween.h"
#include "scene/audio/audio_player.h"
#include "scene/gui/box_container.h"
@@ -133,6 +135,7 @@
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/cylinder_shape.h"
#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/dynamic_font.h"
#include "scene/resources/dynamic_font_stb.h"
@@ -156,10 +159,13 @@
#include "scene/resources/texture.h"
#include "scene/resources/tile_set.h"
#include "scene/resources/video_stream.h"
+#include "scene/resources/visual_shader.h"
+#include "scene/resources/visual_shader_nodes.h"
#include "scene/resources/world.h"
#include "scene/resources/world_2d.h"
#include "scene/scene_string_names.h"
+#include "scene/3d/cpu_particles.h"
#include "scene/3d/particles.h"
#include "scene/3d/scenario_fx.h"
#include "scene/3d/spatial.h"
@@ -193,10 +199,12 @@
#include "scene/3d/remote_transform.h"
#include "scene/3d/room_instance.h"
#include "scene/3d/skeleton.h"
+#include "scene/3d/soft_body.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
#include "scene/resources/environment.h"
+#include "scene/resources/physics_material.h"
#endif
static ResourceFormatLoaderTheme *resource_loader_theme = NULL;
@@ -306,21 +314,19 @@ void register_scene_types() {
ClassDB::register_class<CenterContainer>();
ClassDB::register_class<ScrollContainer>();
ClassDB::register_class<PanelContainer>();
- ClassDB::register_virtual_class<SplitContainer>();
- ClassDB::register_class<HSplitContainer>();
- ClassDB::register_class<VSplitContainer>();
- ClassDB::register_class<GraphNode>();
- ClassDB::register_class<GraphEdit>();
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_class<TextureProgress>();
ClassDB::register_class<ItemList>();
+ ClassDB::register_class<LineEdit>();
+ ClassDB::register_class<VideoPlayer>();
+
#ifndef ADVANCED_GUI_DISABLED
ClassDB::register_class<FileDialog>();
- ClassDB::register_class<LineEdit>();
+
ClassDB::register_class<PopupMenu>();
ClassDB::register_class<Tree>();
@@ -337,9 +343,13 @@ void register_scene_types() {
ClassDB::register_class<WindowDialog>();
ClassDB::register_class<AcceptDialog>();
ClassDB::register_class<ConfirmationDialog>();
- ClassDB::register_class<VideoPlayer>();
ClassDB::register_class<MarginContainer>();
ClassDB::register_class<ViewportContainer>();
+ ClassDB::register_virtual_class<SplitContainer>();
+ ClassDB::register_class<HSplitContainer>();
+ ClassDB::register_class<VSplitContainer>();
+ ClassDB::register_class<GraphNode>();
+ ClassDB::register_class<GraphEdit>();
OS::get_singleton()->yield(); //may take time to init
@@ -382,11 +392,15 @@ void register_scene_types() {
ClassDB::register_class<BakedLightmapData>();
ClassDB::register_class<AnimationTreePlayer>();
ClassDB::register_class<Particles>();
+ ClassDB::register_class<CPUParticles>();
ClassDB::register_class<Position3D>();
ClassDB::register_class<NavigationMeshInstance>();
ClassDB::register_class<NavigationMesh>();
ClassDB::register_class<Navigation>();
+ ClassDB::register_class<RootMotionView>();
+ ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor
+
ClassDB::register_class<AnimationTree>();
ClassDB::register_class<AnimationNode>();
ClassDB::register_class<AnimationRootNode>();
@@ -398,7 +412,8 @@ void register_scene_types() {
ClassDB::register_class<AnimationNodeOutput>();
ClassDB::register_class<AnimationNodeOneShot>();
ClassDB::register_class<AnimationNodeAnimation>();
- ClassDB::register_class<AnimationNodeAdd>();
+ ClassDB::register_class<AnimationNodeAdd2>();
+ ClassDB::register_class<AnimationNodeAdd3>();
ClassDB::register_class<AnimationNodeBlend2>();
ClassDB::register_class<AnimationNodeBlend3>();
ClassDB::register_class<AnimationNodeTimeScale>();
@@ -414,6 +429,7 @@ void register_scene_types() {
ClassDB::register_class<KinematicCollision>();
ClassDB::register_class<KinematicBody>();
ClassDB::register_class<PhysicalBone>();
+ ClassDB::register_class<SoftBody>();
ClassDB::register_class<VehicleBody>();
ClassDB::register_class<VehicleWheel>();
@@ -447,6 +463,39 @@ void register_scene_types() {
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
ClassDB::register_class<Shader>();
+ ClassDB::register_class<VisualShader>();
+ ClassDB::register_virtual_class<VisualShaderNode>();
+ ClassDB::register_class<VisualShaderNodeInput>();
+ ClassDB::register_virtual_class<VisualShaderNodeOutput>();
+ ClassDB::register_class<VisualShaderNodeScalarConstant>();
+ ClassDB::register_class<VisualShaderNodeColorConstant>();
+ ClassDB::register_class<VisualShaderNodeVec3Constant>();
+ ClassDB::register_class<VisualShaderNodeTransformConstant>();
+ ClassDB::register_class<VisualShaderNodeScalarOp>();
+ ClassDB::register_class<VisualShaderNodeVectorOp>();
+ ClassDB::register_class<VisualShaderNodeColorOp>();
+ ClassDB::register_class<VisualShaderNodeTransformMult>();
+ ClassDB::register_class<VisualShaderNodeTransformVecMult>();
+ ClassDB::register_class<VisualShaderNodeScalarFunc>();
+ ClassDB::register_class<VisualShaderNodeVectorFunc>();
+ ClassDB::register_class<VisualShaderNodeDotProduct>();
+ ClassDB::register_class<VisualShaderNodeVectorLen>();
+ ClassDB::register_class<VisualShaderNodeScalarInterp>();
+ ClassDB::register_class<VisualShaderNodeVectorInterp>();
+ ClassDB::register_class<VisualShaderNodeVectorCompose>();
+ ClassDB::register_class<VisualShaderNodeTransformCompose>();
+ ClassDB::register_class<VisualShaderNodeVectorDecompose>();
+ ClassDB::register_class<VisualShaderNodeTransformDecompose>();
+ ClassDB::register_class<VisualShaderNodeTexture>();
+ ClassDB::register_class<VisualShaderNodeCubeMap>();
+ ClassDB::register_virtual_class<VisualShaderNodeUniform>();
+ ClassDB::register_class<VisualShaderNodeScalarUniform>();
+ ClassDB::register_class<VisualShaderNodeColorUniform>();
+ ClassDB::register_class<VisualShaderNodeVec3Uniform>();
+ ClassDB::register_class<VisualShaderNodeTransformUniform>();
+ ClassDB::register_class<VisualShaderNodeTextureUniform>();
+ ClassDB::register_class<VisualShaderNodeCubeMapUniform>();
+
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
ClassDB::register_class<CanvasItemMaterial>();
@@ -533,6 +582,7 @@ void register_scene_types() {
ClassDB::register_class<SphereShape>();
ClassDB::register_class<BoxShape>();
ClassDB::register_class<CapsuleShape>();
+ ClassDB::register_class<CylinderShape>();
ClassDB::register_class<PlaneShape>();
ClassDB::register_class<ConvexPolygonShape>();
ClassDB::register_class<ConcavePolygonShape>();
@@ -543,6 +593,8 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_class<SpatialVelocityTracker>();
+
+ ClassDB::register_class<PhysicsMaterial>();
#endif
ClassDB::register_class<World>();
ClassDB::register_class<Environment>();
@@ -558,6 +610,7 @@ void register_scene_types() {
ClassDB::register_class<CurveTexture>();
ClassDB::register_class<GradientTexture>();
ClassDB::register_class<ProxyTexture>();
+ ClassDB::register_class<AnimatedTexture>();
ClassDB::register_class<CubeMap>();
ClassDB::register_class<Animation>();
ClassDB::register_virtual_class<Font>();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index fe4d687c23..8acfdc482a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -134,8 +134,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
int um = d["update"];
if (um < 0)
um = 0;
- else if (um > 2)
- um = 2;
+ else if (um > 3)
+ um = 3;
vt->update_mode = UpdateMode(um);
}
@@ -2329,13 +2329,14 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
int iterations = 10;
- float low = 0;
- float high = bt->values[idx + 1].time - bt->values[idx].time;
+ float duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
+ float low = 0; // 0% of the current animation segment
+ float high = 1; // 100% of the current animation segment
float middle = 0;
Vector2 start(0, bt->values[idx].value.value);
Vector2 start_out = start + bt->values[idx].value.out_handle;
- Vector2 end(high, bt->values[idx + 1].value.value);
+ Vector2 end(duration, bt->values[idx + 1].value.value);
Vector2 end_in = end + bt->values[idx + 1].value.in_handle;
//narrow high and low as much as possible
@@ -2355,7 +2356,6 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
//interpolate the result:
Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end);
Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end);
-
float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
return low_pos.linear_interpolate(high_pos, c).y;
@@ -2363,7 +2363,6 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
int Animation::audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset, float p_end_offset) {
- print_line("really insert key? ");
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1);
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 7f902fc982..f2fd919f20 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -479,6 +479,16 @@ real_t Curve::interpolate_baked(real_t offset) {
}
}
+void Curve::ensure_default_setup(float p_min, float p_max) {
+ if (_points.size() == 0 && _min_value == 0 && _max_value == 1) {
+
+ add_point(Vector2(0, 1));
+ add_point(Vector2(1, 1));
+ set_min_value(p_min);
+ set_max_value(p_max);
+ }
+}
+
void Curve::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_point", "position", "left_tangent", "right_tangent", "left_mode", "right_mode"), &Curve::add_point, DEFVAL(0), DEFVAL(0), DEFVAL(TANGENT_FREE), DEFVAL(TANGENT_FREE));
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 9cb12a4345..058c4f1bc2 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -128,6 +128,8 @@ public:
void set_bake_resolution(int p_resolution);
real_t interpolate_baked(real_t offset);
+ void ensure_default_setup(float p_min, float p_max);
+
protected:
static void _bind_methods();
diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp
new file mode 100644
index 0000000000..f760462d49
--- /dev/null
+++ b/scene/resources/cylinder_shape.cpp
@@ -0,0 +1,116 @@
+/*************************************************************************/
+/* cylinder_shape.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cylinder_shape.h"
+#include "servers/physics_server.h"
+
+Vector<Vector3> CylinderShape::_gen_debug_mesh_lines() {
+
+ float radius = get_radius();
+ float height = get_height();
+
+ Vector<Vector3> points;
+
+ Vector3 d(0, height * 0.5, 0);
+ for (int i = 0; i < 360; i++) {
+
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(b.x, 0, b.y) + d);
+
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ points.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 90 == 0) {
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ return points;
+}
+
+void CylinderShape::_update_shape() {
+
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+}
+
+void CylinderShape::set_radius(float p_radius) {
+
+ radius = p_radius;
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("radius");
+}
+
+float CylinderShape::get_radius() const {
+
+ return radius;
+}
+
+void CylinderShape::set_height(float p_height) {
+
+ height = p_height;
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("height");
+}
+
+float CylinderShape::get_height() const {
+
+ return height;
+}
+
+void CylinderShape::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CylinderShape::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CylinderShape::get_radius);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape::get_height);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+}
+
+CylinderShape::CylinderShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CYLINDER)) {
+
+ radius = 1.0;
+ height = 2.0;
+ _update_shape();
+}
diff --git a/scene/resources/cylinder_shape.h b/scene/resources/cylinder_shape.h
new file mode 100644
index 0000000000..f510758e91
--- /dev/null
+++ b/scene/resources/cylinder_shape.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* cylinder_shape.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CYLINDER_SHAPE_H
+#define CYLINDER_SHAPE_H
+
+#include "scene/resources/shape.h"
+
+class CylinderShape : public Shape {
+
+ GDCLASS(CylinderShape, Shape);
+ float radius;
+ float height;
+
+protected:
+ static void _bind_methods();
+ virtual void _update_shape();
+
+ virtual Vector<Vector3> _gen_debug_mesh_lines();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+ void set_height(float p_height);
+ float get_height() const;
+
+ CylinderShape();
+};
+
+#endif // CYLINDER_SHAPE_H
diff --git a/scene/resources/default_theme/arrow_down.png b/scene/resources/default_theme/arrow_down.png
index fc837d120a..bfb87a4761 100644
--- a/scene/resources/default_theme/arrow_down.png
+++ b/scene/resources/default_theme/arrow_down.png
Binary files differ
diff --git a/scene/resources/default_theme/arrow_right.png b/scene/resources/default_theme/arrow_right.png
index ebe6e26ace..1e4c8e5529 100644
--- a/scene/resources/default_theme/arrow_right.png
+++ b/scene/resources/default_theme/arrow_right.png
Binary files differ
diff --git a/scene/resources/default_theme/background.png b/scene/resources/default_theme/background.png
index 03cfab1de3..6c5f43e3ce 100644
--- a/scene/resources/default_theme/background.png
+++ b/scene/resources/default_theme/background.png
Binary files differ
diff --git a/scene/resources/default_theme/base_green.png b/scene/resources/default_theme/base_green.png
index d03d6f08d8..03a5b313d7 100644
--- a/scene/resources/default_theme/base_green.png
+++ b/scene/resources/default_theme/base_green.png
Binary files differ
diff --git a/scene/resources/default_theme/button_disabled.png b/scene/resources/default_theme/button_disabled.png
index d75e76989d..708748dfe9 100644
--- a/scene/resources/default_theme/button_disabled.png
+++ b/scene/resources/default_theme/button_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/button_focus.png b/scene/resources/default_theme/button_focus.png
index 44e354be95..70e16b953b 100644
--- a/scene/resources/default_theme/button_focus.png
+++ b/scene/resources/default_theme/button_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/button_hover.png b/scene/resources/default_theme/button_hover.png
index 6e609f435f..ef226e3caf 100644
--- a/scene/resources/default_theme/button_hover.png
+++ b/scene/resources/default_theme/button_hover.png
Binary files differ
diff --git a/scene/resources/default_theme/button_normal.png b/scene/resources/default_theme/button_normal.png
index 92482aaf28..7d0bd16221 100644
--- a/scene/resources/default_theme/button_normal.png
+++ b/scene/resources/default_theme/button_normal.png
Binary files differ
diff --git a/scene/resources/default_theme/checked.png b/scene/resources/default_theme/checked.png
index 93e291a29e..bde031b6a2 100644
--- a/scene/resources/default_theme/checked.png
+++ b/scene/resources/default_theme/checked.png
Binary files differ
diff --git a/scene/resources/default_theme/checker_bg.png b/scene/resources/default_theme/checker_bg.png
index f58dfed29c..3eff2f0e08 100644
--- a/scene/resources/default_theme/checker_bg.png
+++ b/scene/resources/default_theme/checker_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/close.png b/scene/resources/default_theme/close.png
index 5ac6357dcd..4d4ac4a551 100644
--- a/scene/resources/default_theme/close.png
+++ b/scene/resources/default_theme/close.png
Binary files differ
diff --git a/scene/resources/default_theme/close_hl.png b/scene/resources/default_theme/close_hl.png
index 5ac6357dcd..4d4ac4a551 100644
--- a/scene/resources/default_theme/close_hl.png
+++ b/scene/resources/default_theme/close_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/color_picker_sample.png b/scene/resources/default_theme/color_picker_sample.png
index b145a3e384..e6ec28d307 100644
--- a/scene/resources/default_theme/color_picker_sample.png
+++ b/scene/resources/default_theme/color_picker_sample.png
Binary files differ
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index d64e6970bf..fe12e2f5f6 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -476,6 +476,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("symbol_color", "TextEdit", control_font_color_hover);
theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2));
theme->set_color("line_number_color", "TextEdit", Color::html("66aaaaaa"));
+ theme->set_color("safe_line_number_color", "TextEdit", Color::html("99aac8aa"));
theme->set_color("function_color", "TextEdit", Color::html("66a2ce"));
theme->set_color("member_variable_color", "TextEdit", Color::html("e64e59"));
theme->set_color("number_color", "TextEdit", Color::html("EB9532"));
@@ -585,6 +586,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4));
theme->set_stylebox("hover", "PopupMenu", selected);
theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3));
+ theme->set_stylebox("labeled_separator_left", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
+ theme->set_stylebox("labeled_separator_right", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
@@ -844,7 +847,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("separation", "HBoxContainer", 4 * scale);
theme->set_constant("separation", "VBoxContainer", 4 * scale);
- theme->set_constant("margin_left", "MarginContainer", 8 * scale);
+ theme->set_constant("margin_left", "MarginContainer", 0 * scale);
theme->set_constant("margin_top", "MarginContainer", 0 * scale);
theme->set_constant("margin_right", "MarginContainer", 0 * scale);
theme->set_constant("margin_bottom", "MarginContainer", 0 * scale);
diff --git a/scene/resources/default_theme/dosfont.png b/scene/resources/default_theme/dosfont.png
index 814d2e9060..e2739b94ea 100644
--- a/scene/resources/default_theme/dosfont.png
+++ b/scene/resources/default_theme/dosfont.png
Binary files differ
diff --git a/scene/resources/default_theme/dropdown.png b/scene/resources/default_theme/dropdown.png
index 3a6a2ed778..b5d9ffbbb4 100644
--- a/scene/resources/default_theme/dropdown.png
+++ b/scene/resources/default_theme/dropdown.png
Binary files differ
diff --git a/scene/resources/default_theme/error_icon.png b/scene/resources/default_theme/error_icon.png
index f291362350..7741d00749 100644
--- a/scene/resources/default_theme/error_icon.png
+++ b/scene/resources/default_theme/error_icon.png
Binary files differ
diff --git a/scene/resources/default_theme/focus.png b/scene/resources/default_theme/focus.png
index 5d37028f2d..f51ea89e8f 100644
--- a/scene/resources/default_theme/focus.png
+++ b/scene/resources/default_theme/focus.png
Binary files differ
diff --git a/scene/resources/default_theme/frame_focus.png b/scene/resources/default_theme/frame_focus.png
index 9170db38ed..1b24ba47d8 100644
--- a/scene/resources/default_theme/frame_focus.png
+++ b/scene/resources/default_theme/frame_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/full_panel_bg.png b/scene/resources/default_theme/full_panel_bg.png
index 7f02dc7259..85f753cc13 100644
--- a/scene/resources/default_theme/full_panel_bg.png
+++ b/scene/resources/default_theme/full_panel_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_breakpoint.png b/scene/resources/default_theme/graph_node_breakpoint.png
index 0e36f31bd4..e18c6f42e1 100644
--- a/scene/resources/default_theme/graph_node_breakpoint.png
+++ b/scene/resources/default_theme/graph_node_breakpoint.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_close.png b/scene/resources/default_theme/graph_node_close.png
index 144a8b9c4c..5c962ae1c6 100644
--- a/scene/resources/default_theme/graph_node_close.png
+++ b/scene/resources/default_theme/graph_node_close.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment.png b/scene/resources/default_theme/graph_node_comment.png
index f2d6daa259..cdec1d1eac 100644
--- a/scene/resources/default_theme/graph_node_comment.png
+++ b/scene/resources/default_theme/graph_node_comment.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment_focus.png b/scene/resources/default_theme/graph_node_comment_focus.png
index a4b7b5a618..472a6b6f53 100644
--- a/scene/resources/default_theme/graph_node_comment_focus.png
+++ b/scene/resources/default_theme/graph_node_comment_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default.png b/scene/resources/default_theme/graph_node_default.png
index e3a220301f..359bbdc205 100644
--- a/scene/resources/default_theme/graph_node_default.png
+++ b/scene/resources/default_theme/graph_node_default.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default_focus.png b/scene/resources/default_theme/graph_node_default_focus.png
index 9972b07593..204dd16ac0 100644
--- a/scene/resources/default_theme/graph_node_default_focus.png
+++ b/scene/resources/default_theme/graph_node_default_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_position.png b/scene/resources/default_theme/graph_node_position.png
index 7ec15e2ff4..24c2759be6 100644
--- a/scene/resources/default_theme/graph_node_position.png
+++ b/scene/resources/default_theme/graph_node_position.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_selected.png b/scene/resources/default_theme/graph_node_selected.png
index f76c9703dd..cc4eb7f753 100644
--- a/scene/resources/default_theme/graph_node_selected.png
+++ b/scene/resources/default_theme/graph_node_selected.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_port.png b/scene/resources/default_theme/graph_port.png
index 9d5082cfdb..f33ae3baf3 100644
--- a/scene/resources/default_theme/graph_port.png
+++ b/scene/resources/default_theme/graph_port.png
Binary files differ
diff --git a/scene/resources/default_theme/hseparator.png b/scene/resources/default_theme/hseparator.png
index 99609ac118..d4fd71ace5 100644
--- a/scene/resources/default_theme/hseparator.png
+++ b/scene/resources/default_theme/hseparator.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_bg.png b/scene/resources/default_theme/hslider_bg.png
index 9c2a2df62a..b402bd370d 100644
--- a/scene/resources/default_theme/hslider_bg.png
+++ b/scene/resources/default_theme/hslider_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber.png b/scene/resources/default_theme/hslider_grabber.png
index 2acd33879a..d273b491ee 100644
--- a/scene/resources/default_theme/hslider_grabber.png
+++ b/scene/resources/default_theme/hslider_grabber.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_disabled.png b/scene/resources/default_theme/hslider_grabber_disabled.png
index 0d75182b8f..dddd1a468e 100644
--- a/scene/resources/default_theme/hslider_grabber_disabled.png
+++ b/scene/resources/default_theme/hslider_grabber_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_hl.png b/scene/resources/default_theme/hslider_grabber_hl.png
index f8a011e64b..e3defb3610 100644
--- a/scene/resources/default_theme/hslider_grabber_hl.png
+++ b/scene/resources/default_theme/hslider_grabber_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_tick.png b/scene/resources/default_theme/hslider_tick.png
index f7afd78529..1ba19c37a1 100644
--- a/scene/resources/default_theme/hslider_tick.png
+++ b/scene/resources/default_theme/hslider_tick.png
Binary files differ
diff --git a/scene/resources/default_theme/hsplit_bg.png b/scene/resources/default_theme/hsplit_bg.png
index 7dd1d48b29..a5749f6d5c 100644
--- a/scene/resources/default_theme/hsplit_bg.png
+++ b/scene/resources/default_theme/hsplit_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/hsplitter.png b/scene/resources/default_theme/hsplitter.png
index 71a3914d7e..2287753c9d 100644
--- a/scene/resources/default_theme/hsplitter.png
+++ b/scene/resources/default_theme/hsplitter.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_add.png b/scene/resources/default_theme/icon_add.png
index fa675045bc..eccb69b363 100644
--- a/scene/resources/default_theme/icon_add.png
+++ b/scene/resources/default_theme/icon_add.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_close.png b/scene/resources/default_theme/icon_close.png
index 5ac6357dcd..4d4ac4a551 100644
--- a/scene/resources/default_theme/icon_close.png
+++ b/scene/resources/default_theme/icon_close.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_color_pick.png b/scene/resources/default_theme/icon_color_pick.png
index 15679a9558..46953febb8 100644
--- a/scene/resources/default_theme/icon_color_pick.png
+++ b/scene/resources/default_theme/icon_color_pick.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_folder.png b/scene/resources/default_theme/icon_folder.png
index cc05e98ebb..d1b308e88d 100644
--- a/scene/resources/default_theme/icon_folder.png
+++ b/scene/resources/default_theme/icon_folder.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_parent_folder.png b/scene/resources/default_theme/icon_parent_folder.png
index 47fee1ad81..35d218722e 100644
--- a/scene/resources/default_theme/icon_parent_folder.png
+++ b/scene/resources/default_theme/icon_parent_folder.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_play.png b/scene/resources/default_theme/icon_play.png
index 864e4e4fb9..b9ed6e6d5b 100644
--- a/scene/resources/default_theme/icon_play.png
+++ b/scene/resources/default_theme/icon_play.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_reload.png b/scene/resources/default_theme/icon_reload.png
index 9303fabb9c..bec5f3f4f9 100644
--- a/scene/resources/default_theme/icon_reload.png
+++ b/scene/resources/default_theme/icon_reload.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_snap_grid.png b/scene/resources/default_theme/icon_snap_grid.png
index 44db9bdfdc..0680317d86 100644
--- a/scene/resources/default_theme/icon_snap_grid.png
+++ b/scene/resources/default_theme/icon_snap_grid.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_stop.png b/scene/resources/default_theme/icon_stop.png
index 1f194d0e14..0c1371ceb9 100644
--- a/scene/resources/default_theme/icon_stop.png
+++ b/scene/resources/default_theme/icon_stop.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_less.png b/scene/resources/default_theme/icon_zoom_less.png
index 888ddc995d..03119c60ca 100644
--- a/scene/resources/default_theme/icon_zoom_less.png
+++ b/scene/resources/default_theme/icon_zoom_less.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_more.png b/scene/resources/default_theme/icon_zoom_more.png
index fa675045bc..31467ec3de 100644
--- a/scene/resources/default_theme/icon_zoom_more.png
+++ b/scene/resources/default_theme/icon_zoom_more.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_reset.png b/scene/resources/default_theme/icon_zoom_reset.png
index 953ae47d24..cac68c09fa 100644
--- a/scene/resources/default_theme/icon_zoom_reset.png
+++ b/scene/resources/default_theme/icon_zoom_reset.png
Binary files differ
diff --git a/scene/resources/default_theme/line_edit.png b/scene/resources/default_theme/line_edit.png
index bf2b91f1be..2b0c506f34 100644
--- a/scene/resources/default_theme/line_edit.png
+++ b/scene/resources/default_theme/line_edit.png
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_disabled.png b/scene/resources/default_theme/line_edit_disabled.png
index a0fa505e4c..69d78febd9 100644
--- a/scene/resources/default_theme/line_edit_disabled.png
+++ b/scene/resources/default_theme/line_edit_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_focus.png b/scene/resources/default_theme/line_edit_focus.png
index e66d7b60e3..1d74b74068 100644
--- a/scene/resources/default_theme/line_edit_focus.png
+++ b/scene/resources/default_theme/line_edit_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/logo.png b/scene/resources/default_theme/logo.png
index 2161402438..d0ef9d8aa7 100644
--- a/scene/resources/default_theme/logo.png
+++ b/scene/resources/default_theme/logo.png
Binary files differ
diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py
index db449f9417..73b1ae0b0b 100755
--- a/scene/resources/default_theme/make_header.py
+++ b/scene/resources/default_theme/make_header.py
@@ -4,15 +4,16 @@ import os
import glob
import string
+enc = "utf-8"
# Generate include files
f = open("theme_data.h", "wb")
-f.write("// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!\n")
+f.write(b"// THIS FILE HAS BEEN AUTOGENERATED, DON\'T EDIT!!\n")
# Generate png image block
-f.write("\n// png image block\n");
+f.write(b"\n// png image block\n")
pixmaps = glob.glob("*.png")
pixmaps.sort()
@@ -21,22 +22,23 @@ for x in pixmaps:
var_str = x[:-4] + "_png"
- f.write("\nstatic const unsigned char " + var_str + "[] = {\n\t")
+ s = "\nstatic const unsigned char " + var_str + "[] = {\n\t"
+ f.write(s.encode(enc))
pngf = open(x, "rb")
b = pngf.read(1)
while(len(b) == 1):
- f.write(hex(ord(b)))
+ f.write(hex(ord(b)).encode(enc))
b = pngf.read(1)
if (len(b) == 1):
- f.write(", ")
+ f.write(b", ")
- f.write("\n};\n")
+ f.write(b"\n};\n")
pngf.close()
# Generate shaders block
-f.write("\n// shaders block\n");
+f.write(b"\n// shaders block\n");
shaders = glob.glob("*.gsl")
shaders.sort()
@@ -45,7 +47,8 @@ for x in shaders:
var_str = x[:-4] + "_shader_code"
- f.write("\nstatic const char *" + var_str + " = \n")
+ s = "\nstatic const char *" + var_str + " = \n"
+ f.write(s.encode(enc))
sf = open(x, "rb")
@@ -55,12 +58,13 @@ for x in shaders:
b = b[:-2]
if (b.endswith("\n")):
b = b[:-1]
- f.write(" \"" + b)
+ s = ' \"' + b
+ f.write(s.encode(enc))
b = sf.readline()
if (b != ""):
- f.write("\"\n")
+ f.write(b'"\n')
- f.write("\";\n")
+ f.write(b'";\n')
sf.close()
f.close()
diff --git a/scene/resources/default_theme/mini_checkerboard.png b/scene/resources/default_theme/mini_checkerboard.png
index 3e53183847..d8279bda80 100644
--- a/scene/resources/default_theme/mini_checkerboard.png
+++ b/scene/resources/default_theme/mini_checkerboard.png
Binary files differ
diff --git a/scene/resources/default_theme/option_arrow.png b/scene/resources/default_theme/option_arrow.png
index 007de16bfa..40590fd60a 100644
--- a/scene/resources/default_theme/option_arrow.png
+++ b/scene/resources/default_theme/option_arrow.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_disabled.png b/scene/resources/default_theme/option_button_disabled.png
index ce727d56e1..1961b673cd 100644
--- a/scene/resources/default_theme/option_button_disabled.png
+++ b/scene/resources/default_theme/option_button_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_focus.png b/scene/resources/default_theme/option_button_focus.png
index c76d91287e..402670f9a2 100644
--- a/scene/resources/default_theme/option_button_focus.png
+++ b/scene/resources/default_theme/option_button_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover.png b/scene/resources/default_theme/option_button_hover.png
index fd1e987ceb..826fe1c9ca 100644
--- a/scene/resources/default_theme/option_button_hover.png
+++ b/scene/resources/default_theme/option_button_hover.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal.png b/scene/resources/default_theme/option_button_normal.png
index 9d7fb98d1c..2dadb40338 100644
--- a/scene/resources/default_theme/option_button_normal.png
+++ b/scene/resources/default_theme/option_button_normal.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed.png b/scene/resources/default_theme/option_button_pressed.png
index 28b1d93468..68796f9d85 100644
--- a/scene/resources/default_theme/option_button_pressed.png
+++ b/scene/resources/default_theme/option_button_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/panel_bg.png b/scene/resources/default_theme/panel_bg.png
index 320819ad6d..b496e2177e 100644
--- a/scene/resources/default_theme/panel_bg.png
+++ b/scene/resources/default_theme/panel_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg.png b/scene/resources/default_theme/popup_bg.png
index 63f5994441..023029f936 100644
--- a/scene/resources/default_theme/popup_bg.png
+++ b/scene/resources/default_theme/popup_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg_disabled.png b/scene/resources/default_theme/popup_bg_disabled.png
index 611d949380..8eab5f27bc 100644
--- a/scene/resources/default_theme/popup_bg_disabled.png
+++ b/scene/resources/default_theme/popup_bg_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_checked.png b/scene/resources/default_theme/popup_checked.png
index a24e0543c0..b7b05640e1 100644
--- a/scene/resources/default_theme/popup_checked.png
+++ b/scene/resources/default_theme/popup_checked.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_hover.png b/scene/resources/default_theme/popup_hover.png
index 85d4e48475..bdb6ae8bd0 100644
--- a/scene/resources/default_theme/popup_hover.png
+++ b/scene/resources/default_theme/popup_hover.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_unchecked.png b/scene/resources/default_theme/popup_unchecked.png
index c1137e6fbf..ff922335c3 100644
--- a/scene/resources/default_theme/popup_unchecked.png
+++ b/scene/resources/default_theme/popup_unchecked.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_window.png b/scene/resources/default_theme/popup_window.png
index 59362a8ffd..174a29ef45 100644
--- a/scene/resources/default_theme/popup_window.png
+++ b/scene/resources/default_theme/popup_window.png
Binary files differ
diff --git a/scene/resources/default_theme/progress_bar.png b/scene/resources/default_theme/progress_bar.png
index bf81e3adea..057557e567 100644
--- a/scene/resources/default_theme/progress_bar.png
+++ b/scene/resources/default_theme/progress_bar.png
Binary files differ
diff --git a/scene/resources/default_theme/progress_fill.png b/scene/resources/default_theme/progress_fill.png
index 3a34dfdda6..e39bb2a021 100644
--- a/scene/resources/default_theme/progress_fill.png
+++ b/scene/resources/default_theme/progress_fill.png
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked.png b/scene/resources/default_theme/radio_checked.png
index 95d472022f..0ce575c15f 100644
--- a/scene/resources/default_theme/radio_checked.png
+++ b/scene/resources/default_theme/radio_checked.png
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked.png b/scene/resources/default_theme/radio_unchecked.png
index 7f0535c3a4..fe5bcf6ab1 100644
--- a/scene/resources/default_theme/radio_unchecked.png
+++ b/scene/resources/default_theme/radio_unchecked.png
Binary files differ
diff --git a/scene/resources/default_theme/reference_border.png b/scene/resources/default_theme/reference_border.png
index 96219676bf..6a680f393c 100644
--- a/scene/resources/default_theme/reference_border.png
+++ b/scene/resources/default_theme/reference_border.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_bg.png b/scene/resources/default_theme/scroll_bg.png
index cefadb2c08..fb151a48b1 100644
--- a/scene/resources/default_theme/scroll_bg.png
+++ b/scene/resources/default_theme/scroll_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_down.png b/scene/resources/default_theme/scroll_button_down.png
index caeac9b286..1df4ef5b6b 100644
--- a/scene/resources/default_theme/scroll_button_down.png
+++ b/scene/resources/default_theme/scroll_button_down.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_down_hl.png b/scene/resources/default_theme/scroll_button_down_hl.png
index 48036e0297..ba79087393 100644
--- a/scene/resources/default_theme/scroll_button_down_hl.png
+++ b/scene/resources/default_theme/scroll_button_down_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left.png b/scene/resources/default_theme/scroll_button_left.png
index 3b50938d97..e430cb4673 100644
--- a/scene/resources/default_theme/scroll_button_left.png
+++ b/scene/resources/default_theme/scroll_button_left.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left_hl.png b/scene/resources/default_theme/scroll_button_left_hl.png
index b3d348c24f..2a6ef17a34 100644
--- a/scene/resources/default_theme/scroll_button_left_hl.png
+++ b/scene/resources/default_theme/scroll_button_left_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right.png b/scene/resources/default_theme/scroll_button_right.png
index 1c622a41ad..4f61687aa4 100644
--- a/scene/resources/default_theme/scroll_button_right.png
+++ b/scene/resources/default_theme/scroll_button_right.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right_hl.png b/scene/resources/default_theme/scroll_button_right_hl.png
index 108796ca02..10e2722509 100644
--- a/scene/resources/default_theme/scroll_button_right_hl.png
+++ b/scene/resources/default_theme/scroll_button_right_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_up.png b/scene/resources/default_theme/scroll_button_up.png
index 2c8238ae4c..f425412f50 100644
--- a/scene/resources/default_theme/scroll_button_up.png
+++ b/scene/resources/default_theme/scroll_button_up.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_up_hl.png b/scene/resources/default_theme/scroll_button_up_hl.png
index 4283bd114a..615a236c52 100644
--- a/scene/resources/default_theme/scroll_button_up_hl.png
+++ b/scene/resources/default_theme/scroll_button_up_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber.png b/scene/resources/default_theme/scroll_grabber.png
index 1d625a9b7b..732725a28f 100644
--- a/scene/resources/default_theme/scroll_grabber.png
+++ b/scene/resources/default_theme/scroll_grabber.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_hl.png b/scene/resources/default_theme/scroll_grabber_hl.png
index 99eb24b7e7..006cfa3361 100644
--- a/scene/resources/default_theme/scroll_grabber_hl.png
+++ b/scene/resources/default_theme/scroll_grabber_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_pressed.png b/scene/resources/default_theme/scroll_grabber_pressed.png
index a46d242ddd..f4886158fa 100644
--- a/scene/resources/default_theme/scroll_grabber_pressed.png
+++ b/scene/resources/default_theme/scroll_grabber_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/selection.png b/scene/resources/default_theme/selection.png
index 501877a8b4..7d1c985b35 100644
--- a/scene/resources/default_theme/selection.png
+++ b/scene/resources/default_theme/selection.png
Binary files differ
diff --git a/scene/resources/default_theme/selection_oof.png b/scene/resources/default_theme/selection_oof.png
index 9594fe0913..2da0538389 100644
--- a/scene/resources/default_theme/selection_oof.png
+++ b/scene/resources/default_theme/selection_oof.png
Binary files differ
diff --git a/scene/resources/default_theme/spinbox_updown.png b/scene/resources/default_theme/spinbox_updown.png
index b40b1e9fd2..74fab19f34 100644
--- a/scene/resources/default_theme/spinbox_updown.png
+++ b/scene/resources/default_theme/spinbox_updown.png
Binary files differ
diff --git a/scene/resources/default_theme/submenu.png b/scene/resources/default_theme/submenu.png
index ec727eecf1..8f7de446d4 100644
--- a/scene/resources/default_theme/submenu.png
+++ b/scene/resources/default_theme/submenu.png
Binary files differ
diff --git a/scene/resources/default_theme/tab.png b/scene/resources/default_theme/tab.png
index 3e4d792a48..895daa65e2 100644
--- a/scene/resources/default_theme/tab.png
+++ b/scene/resources/default_theme/tab.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_behind.png b/scene/resources/default_theme/tab_behind.png
index 12f07c3a91..2803d9db65 100644
--- a/scene/resources/default_theme/tab_behind.png
+++ b/scene/resources/default_theme/tab_behind.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_close.png b/scene/resources/default_theme/tab_close.png
index 20d9b5c810..af2775a132 100644
--- a/scene/resources/default_theme/tab_close.png
+++ b/scene/resources/default_theme/tab_close.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_container_bg.png b/scene/resources/default_theme/tab_container_bg.png
index 92482aaf28..7d0bd16221 100644
--- a/scene/resources/default_theme/tab_container_bg.png
+++ b/scene/resources/default_theme/tab_container_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_current.png b/scene/resources/default_theme/tab_current.png
index 7289e032da..520d147217 100644
--- a/scene/resources/default_theme/tab_current.png
+++ b/scene/resources/default_theme/tab_current.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu.png b/scene/resources/default_theme/tab_menu.png
index 148b64b8aa..fa4421a28a 100644
--- a/scene/resources/default_theme/tab_menu.png
+++ b/scene/resources/default_theme/tab_menu.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu_hl.png b/scene/resources/default_theme/tab_menu_hl.png
index 148b64b8aa..fa4421a28a 100644
--- a/scene/resources/default_theme/tab_menu_hl.png
+++ b/scene/resources/default_theme/tab_menu_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off.png b/scene/resources/default_theme/toggle_off.png
index 71cd64b001..64b51c8c9d 100644
--- a/scene/resources/default_theme/toggle_off.png
+++ b/scene/resources/default_theme/toggle_off.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on.png b/scene/resources/default_theme/toggle_on.png
index 6ea1b589c7..f0c699c181 100644
--- a/scene/resources/default_theme/toggle_on.png
+++ b/scene/resources/default_theme/toggle_on.png
Binary files differ
diff --git a/scene/resources/default_theme/tool_button_pressed.png b/scene/resources/default_theme/tool_button_pressed.png
index bcf70b486d..5494475792 100644
--- a/scene/resources/default_theme/tool_button_pressed.png
+++ b/scene/resources/default_theme/tool_button_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/tooltip_bg.png b/scene/resources/default_theme/tooltip_bg.png
index eca0675a98..07b7d942ca 100644
--- a/scene/resources/default_theme/tooltip_bg.png
+++ b/scene/resources/default_theme/tooltip_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg.png b/scene/resources/default_theme/tree_bg.png
index 839a6a272a..2b0c506f34 100644
--- a/scene/resources/default_theme/tree_bg.png
+++ b/scene/resources/default_theme/tree_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg_disabled.png b/scene/resources/default_theme/tree_bg_disabled.png
index a0fa505e4c..69d78febd9 100644
--- a/scene/resources/default_theme/tree_bg_disabled.png
+++ b/scene/resources/default_theme/tree_bg_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg_focus.png b/scene/resources/default_theme/tree_bg_focus.png
index 692cf71926..aadc6b0db4 100644
--- a/scene/resources/default_theme/tree_bg_focus.png
+++ b/scene/resources/default_theme/tree_bg_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_cursor.png b/scene/resources/default_theme/tree_cursor.png
index 94d2a08818..2b8722d066 100644
--- a/scene/resources/default_theme/tree_cursor.png
+++ b/scene/resources/default_theme/tree_cursor.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_cursor_unfocus.png b/scene/resources/default_theme/tree_cursor_unfocus.png
index 3f023bbabe..bfaebbea85 100644
--- a/scene/resources/default_theme/tree_cursor_unfocus.png
+++ b/scene/resources/default_theme/tree_cursor_unfocus.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_title.png b/scene/resources/default_theme/tree_title.png
index b0ddcffbbe..e5f3f49695 100644
--- a/scene/resources/default_theme/tree_title.png
+++ b/scene/resources/default_theme/tree_title.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_title_pressed.png b/scene/resources/default_theme/tree_title_pressed.png
index 746d10039e..35e2bb3008 100644
--- a/scene/resources/default_theme/tree_title_pressed.png
+++ b/scene/resources/default_theme/tree_title_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/unchecked.png b/scene/resources/default_theme/unchecked.png
index d6f790cbc2..8c818af755 100644
--- a/scene/resources/default_theme/unchecked.png
+++ b/scene/resources/default_theme/unchecked.png
Binary files differ
diff --git a/scene/resources/default_theme/updown.png b/scene/resources/default_theme/updown.png
index 916284a3cf..56f81921e8 100644
--- a/scene/resources/default_theme/updown.png
+++ b/scene/resources/default_theme/updown.png
Binary files differ
diff --git a/scene/resources/default_theme/vseparator.png b/scene/resources/default_theme/vseparator.png
index 498768c05b..51e79f3ac5 100644
--- a/scene/resources/default_theme/vseparator.png
+++ b/scene/resources/default_theme/vseparator.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_bg.png b/scene/resources/default_theme/vslider_bg.png
index 8d9ead3c5a..ba3244e3e5 100644
--- a/scene/resources/default_theme/vslider_bg.png
+++ b/scene/resources/default_theme/vslider_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber.png b/scene/resources/default_theme/vslider_grabber.png
index afc490be45..6c6bf93e68 100644
--- a/scene/resources/default_theme/vslider_grabber.png
+++ b/scene/resources/default_theme/vslider_grabber.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_disabled.png b/scene/resources/default_theme/vslider_grabber_disabled.png
index c830359f45..49cced5055 100644
--- a/scene/resources/default_theme/vslider_grabber_disabled.png
+++ b/scene/resources/default_theme/vslider_grabber_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_hl.png b/scene/resources/default_theme/vslider_grabber_hl.png
index 548972e115..28774fdbf8 100644
--- a/scene/resources/default_theme/vslider_grabber_hl.png
+++ b/scene/resources/default_theme/vslider_grabber_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_tick.png b/scene/resources/default_theme/vslider_tick.png
index 873ebb00d8..bde788b563 100644
--- a/scene/resources/default_theme/vslider_tick.png
+++ b/scene/resources/default_theme/vslider_tick.png
Binary files differ
diff --git a/scene/resources/default_theme/vsplit_bg.png b/scene/resources/default_theme/vsplit_bg.png
index 7dd1d48b29..a5749f6d5c 100644
--- a/scene/resources/default_theme/vsplit_bg.png
+++ b/scene/resources/default_theme/vsplit_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/vsplitter.png b/scene/resources/default_theme/vsplitter.png
index ec5542bf69..dde1f390df 100644
--- a/scene/resources/default_theme/vsplitter.png
+++ b/scene/resources/default_theme/vsplitter.png
Binary files differ
diff --git a/scene/resources/default_theme/window_resizer.png b/scene/resources/default_theme/window_resizer.png
index ed51968c4e..b06e6f5366 100644
--- a/scene/resources/default_theme/window_resizer.png
+++ b/scene/resources/default_theme/window_resizer.png
Binary files differ
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index 05493d5777..eb7d517841 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -211,9 +211,9 @@ Error DynamicFontAtSize::_load() {
scale_color_font = float(id.size) / face->available_sizes[i].width;
}
}
- error = FT_Select_Size(face, best_match);
+ FT_Select_Size(face, best_match);
} else {
- error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
+ FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
}
ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font;
@@ -625,7 +625,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
break;
}
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
if (error) {
char_map[p_char] = character;
return;
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 3fab4d3cfc..d3da842b79 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -378,7 +378,7 @@ bool Environment::is_ssr_rough() const {
void Environment::set_ssao_enabled(bool p_enable) {
ssao_enabled = p_enable;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
_change_notify();
}
@@ -390,7 +390,7 @@ bool Environment::is_ssao_enabled() const {
void Environment::set_ssao_radius(float p_radius) {
ssao_radius = p_radius;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_radius() const {
@@ -400,7 +400,7 @@ float Environment::get_ssao_radius() const {
void Environment::set_ssao_intensity(float p_intensity) {
ssao_intensity = p_intensity;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_intensity() const {
@@ -411,7 +411,7 @@ float Environment::get_ssao_intensity() const {
void Environment::set_ssao_radius2(float p_radius) {
ssao_radius2 = p_radius;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_radius2() const {
@@ -421,7 +421,7 @@ float Environment::get_ssao_radius2() const {
void Environment::set_ssao_intensity2(float p_intensity) {
ssao_intensity2 = p_intensity;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_intensity2() const {
@@ -431,7 +431,7 @@ float Environment::get_ssao_intensity2() const {
void Environment::set_ssao_bias(float p_bias) {
ssao_bias = p_bias;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_bias() const {
@@ -441,17 +441,27 @@ float Environment::get_ssao_bias() const {
void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) {
ssao_direct_light_affect = p_direct_light_affect;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_direct_light_affect() const {
return ssao_direct_light_affect;
}
+void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) {
+
+ ssao_ao_channel_affect = p_ao_channel_affect;
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+}
+float Environment::get_ssao_ao_channel_affect() const {
+
+ return ssao_ao_channel_affect;
+}
+
void Environment::set_ssao_color(const Color &p_color) {
ssao_color = p_color;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
Color Environment::get_ssao_color() const {
@@ -462,7 +472,7 @@ Color Environment::get_ssao_color() const {
void Environment::set_ssao_blur(SSAOBlur p_blur) {
ssao_blur = p_blur;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
Environment::SSAOBlur Environment::get_ssao_blur() const {
@@ -472,7 +482,7 @@ Environment::SSAOBlur Environment::get_ssao_blur() const {
void Environment::set_ssao_quality(SSAOQuality p_quality) {
ssao_quality = p_quality;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
Environment::SSAOQuality Environment::get_ssao_quality() const {
@@ -483,7 +493,7 @@ Environment::SSAOQuality Environment::get_ssao_quality() const {
void Environment::set_ssao_edge_sharpness(float p_edge_sharpness) {
ssao_edge_sharpness = p_edge_sharpness;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_edge_sharpness() const {
@@ -1008,6 +1018,9 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ssao_direct_light_affect", "amount"), &Environment::set_ssao_direct_light_affect);
ClassDB::bind_method(D_METHOD("get_ssao_direct_light_affect"), &Environment::get_ssao_direct_light_affect);
+ ClassDB::bind_method(D_METHOD("set_ssao_ao_channel_affect", "amount"), &Environment::set_ssao_ao_channel_affect);
+ ClassDB::bind_method(D_METHOD("get_ssao_ao_channel_affect"), &Environment::get_ssao_ao_channel_affect);
+
ClassDB::bind_method(D_METHOD("set_ssao_color", "color"), &Environment::set_ssao_color);
ClassDB::bind_method(D_METHOD("get_ssao_color"), &Environment::get_ssao_color);
@@ -1028,6 +1041,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_intensity2", PROPERTY_HINT_RANGE, "0.0,128,0.1"), "set_ssao_intensity2", "get_ssao_intensity2");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_bias", PROPERTY_HINT_RANGE, "0.001,8,0.001"), "set_ssao_bias", "get_ssao_bias");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ssao_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ssao_color", "get_ssao_color");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_ssao_quality", "get_ssao_quality");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_blur", PROPERTY_HINT_ENUM, "Disabled,1x1,2x2,3x3"), "set_ssao_blur", "get_ssao_blur");
@@ -1220,6 +1234,7 @@ Environment::Environment() {
ssao_intensity2 = 1;
ssao_bias = 0.01;
ssao_direct_light_affect = 0.0;
+ ssao_ao_channel_affect = 0.0;
ssao_blur = SSAO_BLUR_3x3;
set_ssao_edge_sharpness(4);
set_ssao_quality(SSAO_QUALITY_LOW);
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 27fd57aa09..7d66c7e60b 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -127,6 +127,7 @@ private:
float ssao_intensity2;
float ssao_bias;
float ssao_direct_light_affect;
+ float ssao_ao_channel_affect;
Color ssao_color;
SSAOBlur ssao_blur;
float ssao_edge_sharpness;
@@ -274,6 +275,9 @@ public:
void set_ssao_direct_light_affect(float p_direct_light_affect);
float get_ssao_direct_light_affect() const;
+ void set_ssao_ao_channel_affect(float p_ao_channel_affect);
+ float get_ssao_ao_channel_affect() const;
+
void set_ssao_color(const Color &p_color);
Color get_ssao_color() const;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 5e7569586a..56b3ee70bb 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -145,11 +145,17 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
+ if (shader.is_valid()) {
+ shader->disconnect("changed", this, "_shader_changed");
+ }
+
shader = p_shader;
RID rid;
- if (shader.is_valid())
+ if (shader.is_valid()) {
rid = shader->get_rid();
+ shader->connect("changed", this, "_shader_changed");
+ }
VS::get_singleton()->material_set_shader(_get_material(), rid);
_change_notify(); //properties for shader exposed
@@ -171,12 +177,17 @@ Variant ShaderMaterial::get_shader_param(const StringName &p_param) const {
return VS::get_singleton()->material_get_param(_get_material(), p_param);
}
+void ShaderMaterial::_shader_changed() {
+ _change_notify(); //update all properties
+}
+
void ShaderMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader);
ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader);
ClassDB::bind_method(D_METHOD("set_shader_param", "param", "value"), &ShaderMaterial::set_shader_param);
ClassDB::bind_method(D_METHOD("get_shader_param", "param"), &ShaderMaterial::get_shader_param);
+ ClassDB::bind_method(D_METHOD("_shader_changed"), &ShaderMaterial::_shader_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_shader", "get_shader");
}
@@ -393,6 +404,12 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_DONT_RECEIVE_SHADOWS]) {
code += ",shadows_disabled";
}
+ if (flags[FLAG_DISABLE_AMBIENT_LIGHT]) {
+ code += ",ambient_light_disabled";
+ }
+ if (flags[FLAG_ENSURE_CORRECT_NORMALS]) {
+ code += ",ensure_correct_normals";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -1852,6 +1869,8 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_ensure_correct_normals"), "set_flag", "get_flag", FLAG_ENSURE_CORRECT_NORMALS);
ADD_GROUP("Vertex Color", "vertex_color");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_is_srgb"), "set_flag", "get_flag", FLAG_SRGB_VERTEX_COLOR);
@@ -2042,6 +2061,8 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_AMBIENT_LIGHT);
+ BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index ce733bfb8d..84f1005b03 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -92,6 +92,8 @@ protected:
virtual bool _can_do_next_pass() const;
+ void _shader_changed();
+
public:
void set_shader(const Ref<Shader> &p_shader);
Ref<Shader> get_shader() const;
@@ -189,6 +191,8 @@ public:
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
FLAG_DONT_RECEIVE_SHADOWS,
+ FLAG_ENSURE_CORRECT_NORMALS,
+ FLAG_DISABLE_AMBIENT_LIGHT,
FLAG_MAX
};
@@ -237,7 +241,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 15;
+ uint64_t flags : 17;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index b0620d3363..fe87dcdd2c 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -40,7 +40,6 @@
void Mesh::_clear_triangle_mesh() const {
triangle_mesh.unref();
- ;
}
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
@@ -110,6 +109,54 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
return triangle_mesh;
}
+void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) {
+
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ if (tm.is_null())
+ return;
+
+ PoolVector<int> triangle_indices;
+ tm->get_indices(&triangle_indices);
+ const int triangles_num = tm->get_triangles().size();
+ PoolVector<Vector3> vertices = tm->get_vertices();
+
+ r_lines.resize(tm->get_triangles().size() * 6); // 3 lines x 2 points each line
+
+ PoolVector<int>::Read ind_r = triangle_indices.read();
+ PoolVector<Vector3>::Read ver_r = vertices.read();
+ for (int j = 0, x = 0, i = 0; i < triangles_num; j += 6, x += 3, ++i) {
+ // Triangle line 1
+ r_lines[j + 0] = ver_r[ind_r[x + 0]];
+ r_lines[j + 1] = ver_r[ind_r[x + 1]];
+
+ // Triangle line 2
+ r_lines[j + 2] = ver_r[ind_r[x + 1]];
+ r_lines[j + 3] = ver_r[ind_r[x + 2]];
+
+ // Triangle line 3
+ r_lines[j + 4] = ver_r[ind_r[x + 2]];
+ r_lines[j + 5] = ver_r[ind_r[x + 0]];
+ }
+}
+void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) {
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ if (tm.is_null())
+ return;
+
+ PoolVector<Vector3> vertices = tm->get_vertices();
+
+ int vertices_size = vertices.size();
+ r_points.resize(vertices_size);
+ for (int i = 0; i < vertices_size; ++i) {
+ r_points[i] = vertices[i];
+ }
+}
+
+bool Mesh::surface_is_softbody_friendly(int p_idx) const {
+ const uint32_t surface_format = surface_get_format(p_idx);
+ return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_VERTEX)) && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL)));
+}
+
PoolVector<Face3> Mesh::get_faces() const {
Ref<TriangleMesh> tm = generate_triangle_mesh();
@@ -484,6 +531,10 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_MAX);
}
+void Mesh::clear_cache() {
+ _clear_triangle_mesh();
+}
+
Mesh::Mesh() {
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index e8b7ecaf9a..2127eaae4c 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -100,7 +100,7 @@ public:
ARRAY_FLAG_USE_16_BIT_BONES = ARRAY_COMPRESS_INDEX << 2,
ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_VERTEX | ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
+ ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
};
@@ -123,6 +123,7 @@ public:
virtual int get_surface_count() const = 0;
virtual int surface_get_array_len(int p_idx) const = 0;
virtual int surface_get_array_index_len(int p_idx) const = 0;
+ virtual bool surface_is_softbody_friendly(int p_idx) const;
virtual Array surface_get_arrays(int p_surface) const = 0;
virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0;
virtual uint32_t surface_get_format(int p_idx) const = 0;
@@ -133,6 +134,8 @@ public:
PoolVector<Face3> get_faces() const;
Ref<TriangleMesh> generate_triangle_mesh() const;
+ void generate_debug_mesh_lines(Vector<Vector3> &r_lines);
+ void generate_debug_mesh_indices(Vector<Vector3> &r_points);
Ref<Shape> create_trimesh_shape() const;
Ref<Shape> create_convex_shape() const;
@@ -143,6 +146,7 @@ public:
void set_lightmap_size_hint(const Vector2 &p_size);
Size2 get_lightmap_size_hint() const;
+ void clear_cache();
Mesh();
};
diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp
new file mode 100644
index 0000000000..de3cfd1371
--- /dev/null
+++ b/scene/resources/physics_material.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* physics_material.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "physics_material.h"
+
+bool PhysicsMaterial::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "bounce") {
+ set_bounce(p_value);
+ } else if (p_name == "bounce_combine_mode") {
+ set_bounce_combine_mode(static_cast<PhysicsServer::CombineMode>(int(p_value)));
+ } else if (p_name == "friction") {
+ set_friction(p_value);
+ } else if (p_name == "friction_combine_mode") {
+ set_friction_combine_mode(static_cast<PhysicsServer::CombineMode>(int(p_value)));
+ } else {
+ return false;
+ }
+
+ emit_changed();
+ return true;
+}
+
+bool PhysicsMaterial::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "bounce") {
+ r_ret = bounce;
+ } else if (p_name == "bounce_combine_mode") {
+ r_ret = int(bounce_combine_mode);
+ } else if (p_name == "friction") {
+ r_ret = friction;
+ } else if (p_name == "friction_combine_mode") {
+ r_ret = int(friction_combine_mode);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicsMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::REAL, "bounce"));
+ p_list->push_back(PropertyInfo(Variant::INT, "bounce_combine_mode", PROPERTY_HINT_ENUM, "Max,Min,Multiply,Average"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "friction"));
+ p_list->push_back(PropertyInfo(Variant::INT, "friction_combine_mode", PROPERTY_HINT_ENUM, "Max,Min,Multiply,Average"));
+}
+
+void PhysicsMaterial::_bind_methods() {}
+
+void PhysicsMaterial::set_bounce(real_t p_val) {
+ bounce = p_val;
+}
+
+void PhysicsMaterial::set_bounce_combine_mode(PhysicsServer::CombineMode p_val) {
+ bounce_combine_mode = p_val;
+}
+
+void PhysicsMaterial::set_friction(real_t p_val) {
+ friction = p_val;
+}
+
+void PhysicsMaterial::set_friction_combine_mode(PhysicsServer::CombineMode p_val) {
+ friction_combine_mode = p_val;
+}
+
+PhysicsMaterial::PhysicsMaterial() :
+ bounce(0),
+ bounce_combine_mode(PhysicsServer::COMBINE_MODE_MAX),
+ friction(0),
+ friction_combine_mode(PhysicsServer::COMBINE_MODE_MULTIPLY) {}
diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h
new file mode 100644
index 0000000000..a6cb8c288e
--- /dev/null
+++ b/scene/resources/physics_material.h
@@ -0,0 +1,70 @@
+/*************************************************************************/
+/* physics_material.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 physics_material_override_H
+#define physics_material_override_H
+
+#include "resource.h"
+#include "servers/physics_server.h"
+
+class PhysicsMaterial : public Resource {
+
+ GDCLASS(PhysicsMaterial, Resource);
+ OBJ_SAVE_TYPE(PhysicsMaterial);
+ RES_BASE_EXTENSION("PhyMat");
+
+ real_t bounce;
+ PhysicsServer::CombineMode bounce_combine_mode;
+ real_t friction;
+ PhysicsServer::CombineMode friction_combine_mode;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+ void set_bounce(real_t p_val);
+ _FORCE_INLINE_ real_t get_bounce() const { return bounce; }
+
+ void set_bounce_combine_mode(PhysicsServer::CombineMode p_val);
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_bounce_combine_mode() const { return bounce_combine_mode; }
+
+ void set_friction(real_t p_val);
+ _FORCE_INLINE_ real_t get_friction() const { return friction; }
+
+ void set_friction_combine_mode(PhysicsServer::CombineMode p_val);
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_friction_combine_mode() const { return friction_combine_mode; }
+
+ PhysicsMaterial();
+};
+
+#endif // physics_material_override_H
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
index 597866eb74..9df117d09c 100644
--- a/scene/resources/scene_format_text.cpp
+++ b/scene/resources/scene_format_text.cpp
@@ -896,7 +896,7 @@ static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p
CharString utf8 = p_string.utf8();
if (p_bit_on_len) {
- f->store_32(utf8.length() + 1 | 0x80000000);
+ f->store_32((utf8.length() + 1) | 0x80000000);
} else {
f->store_32(utf8.length() + 1);
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 36740a307b..f53f03c1c8 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -54,16 +54,20 @@ void Shader::set_code(const String &p_code) {
VisualServer::get_singleton()->shader_set_code(shader, p_code);
params_cache_dirty = true;
- emit_signal(SceneStringNames::get_singleton()->changed);
+
+ emit_changed();
}
String Shader::get_code() const {
+ _update_shader();
return VisualServer::get_singleton()->shader_get_code(shader);
}
void Shader::get_param_list(List<PropertyInfo> *p_params) const {
+ _update_shader();
+
List<PropertyInfo> local;
VisualServer::get_singleton()->shader_get_param_list(shader, &local);
params_cache.clear();
@@ -72,6 +76,9 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
for (List<PropertyInfo>::Element *E = local.front(); E; E = E->next()) {
PropertyInfo pi = E->get();
+ if (default_textures.has(pi.name)) { //do not show default textures
+ continue;
+ }
pi.name = "shader_param/" + pi.name;
params_cache[pi.name] = E->get().name;
if (p_params) {
@@ -86,6 +93,8 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
RID Shader::get_rid() const {
+ _update_shader();
+
return shader;
}
@@ -98,6 +107,8 @@ void Shader::set_default_texture_param(const StringName &p_param, const Ref<Text
default_textures.erase(p_param);
VS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID());
}
+
+ emit_changed();
}
Ref<Texture> Shader::get_default_texture_param(const StringName &p_param) const {
@@ -120,6 +131,9 @@ bool Shader::has_param(const StringName &p_param) const {
return params_cache.has(p_param);
}
+void Shader::_update_shader() const {
+}
+
void Shader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mode"), &Shader::get_mode);
@@ -227,5 +241,5 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource,
}
bool ResourceFormatSaverShader::recognize(const RES &p_resource) const {
- return Object::cast_to<Shader>(*p_resource) != NULL;
+ return p_resource->get_class_name() == "Shader"; //only shader, not inherited
}
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 248a6f0125..efc5da7753 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -61,12 +61,13 @@ private:
mutable Map<StringName, StringName> params_cache; //map a shader param to a material param..
Map<StringName, Ref<Texture> > default_textures;
+ virtual void _update_shader() const; //used for visual shader
protected:
static void _bind_methods();
public:
//void set_mode(Mode p_mode);
- Mode get_mode() const;
+ virtual Mode get_mode() const;
void set_code(const String &p_code);
String get_code() const;
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index ebad00b068..fb81375b0a 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -905,12 +905,20 @@ bool StyleBoxLine::is_vertical() const {
return vertical;
}
-void StyleBoxLine::set_grow(float p_grow) {
- grow = p_grow;
+void StyleBoxLine::set_grow_end(float p_grow_end) {
+ grow_end = p_grow_end;
emit_changed();
}
-float StyleBoxLine::get_grow() const {
- return grow;
+float StyleBoxLine::get_grow_end() const {
+ return grow_end;
+}
+
+void StyleBoxLine::set_grow_begin(float p_grow_begin) {
+ grow_begin = p_grow_begin;
+ emit_changed();
+}
+float StyleBoxLine::get_grow_begin() const {
+ return grow_begin;
}
void StyleBoxLine::_bind_methods() {
@@ -919,13 +927,16 @@ void StyleBoxLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color);
ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness);
ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness);
- ClassDB::bind_method(D_METHOD("set_grow", "grow"), &StyleBoxLine::set_grow);
- ClassDB::bind_method(D_METHOD("get_grow"), &StyleBoxLine::get_grow);
+ ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin);
+ ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin);
+ ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end);
+ ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end);
ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical);
ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow", "get_grow");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_begin", "get_grow_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_end", "get_grow_end");
ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10"), "set_thickness", "get_thickness");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
}
@@ -941,12 +952,12 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Rect2i r = p_rect;
if (vertical) {
- r.position.y -= grow;
- r.size.y += grow * 2;
+ r.position.y -= grow_begin;
+ r.size.y += (grow_begin + grow_end);
r.size.x = thickness;
} else {
- r.position.x -= grow;
- r.size.x += grow * 2;
+ r.position.x -= grow_begin;
+ r.size.x += (grow_begin + grow_end);
r.size.y = thickness;
}
@@ -954,7 +965,8 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
}
StyleBoxLine::StyleBoxLine() {
- grow = 1.0;
+ grow_begin = 1.0;
+ grow_end = 1.0;
thickness = 1;
color = Color(0.0, 0.0, 0.0);
vertical = false;
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index c1d84fe19f..ed193a1ab4 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -236,7 +236,8 @@ class StyleBoxLine : public StyleBox {
Color color;
int thickness;
bool vertical;
- float grow;
+ float grow_begin;
+ float grow_end;
protected:
virtual float get_style_margin(Margin p_margin) const;
@@ -252,8 +253,11 @@ public:
void set_vertical(bool p_vertical);
bool is_vertical() const;
- void set_grow(float p_grow);
- float get_grow() const;
+ void set_grow_begin(float p_grow);
+ float get_grow_begin() const;
+
+ void set_grow_end(float p_grow);
+ float get_grow_end() const;
virtual Size2 get_center_size() const;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 5a42873d79..3df9ab26a4 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -421,6 +421,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_
Array a = commit_to_arrays();
mesh->add_surface_from_arrays(primitive, a, Array(), p_flags);
+
if (material.is_valid())
mesh->surface_set_material(surface, material);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 54f5aea160..2baad555c0 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1669,3 +1669,208 @@ ProxyTexture::~ProxyTexture() {
VS::get_singleton()->free(proxy);
}
+//////////////////////////////////////////////
+
+void AnimatedTexture::_update_proxy() {
+
+ _THREAD_SAFE_METHOD_
+
+ float delta;
+ if (prev_ticks == 0) {
+ delta = 0;
+ prev_ticks = OS::get_singleton()->get_ticks_usec();
+ } else {
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ delta = float(double(ticks - prev_ticks) / 1000000.0);
+ prev_ticks = ticks;
+ }
+
+ time += delta;
+
+ float limit;
+
+ if (fps == 0) {
+ limit = 0;
+ } else {
+ limit = 1.0 / fps;
+ }
+
+ int iter_max = frame_count;
+ while (iter_max) {
+ float frame_limit = limit + frames[current_frame].delay_sec;
+
+ if (time > frame_limit) {
+ current_frame++;
+ if (current_frame >= frame_count) {
+ current_frame = 0;
+ }
+ time -= frame_limit;
+ } else {
+ break;
+ }
+ iter_max--;
+ }
+
+ if (frames[current_frame].texture.is_valid()) {
+ VisualServer::get_singleton()->texture_set_proxy(proxy, frames[current_frame].texture->get_rid());
+ }
+}
+
+void AnimatedTexture::set_frames(int p_frames) {
+ ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES);
+
+ _THREAD_SAFE_METHOD_
+
+ frame_count = p_frames;
+}
+int AnimatedTexture::get_frames() const {
+ return frame_count;
+}
+
+void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture> &p_texture) {
+ ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
+
+ _THREAD_SAFE_METHOD_
+
+ frames[p_frame].texture = p_texture;
+}
+Ref<Texture> AnimatedTexture::get_frame_texture(int p_frame) const {
+ ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture>());
+
+ _THREAD_SAFE_METHOD_
+
+ return frames[p_frame].texture;
+}
+
+void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) {
+ ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
+
+ _THREAD_SAFE_METHOD_
+
+ frames[p_frame].delay_sec = p_delay_sec;
+}
+float AnimatedTexture::get_frame_delay(int p_frame) const {
+ ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0);
+
+ _THREAD_SAFE_METHOD_
+
+ return frames[p_frame].delay_sec;
+}
+
+void AnimatedTexture::set_fps(float p_fps) {
+ ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000);
+
+ fps = p_fps;
+}
+float AnimatedTexture::get_fps() const {
+ return fps;
+}
+
+int AnimatedTexture::get_width() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 1;
+ }
+
+ return frames[current_frame].texture->get_width();
+}
+int AnimatedTexture::get_height() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 1;
+ }
+
+ return frames[current_frame].texture->get_height();
+}
+RID AnimatedTexture::get_rid() const {
+ return proxy;
+}
+
+bool AnimatedTexture::has_alpha() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return false;
+ }
+
+ return frames[current_frame].texture->has_alpha();
+}
+
+Ref<Image> AnimatedTexture::get_data() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return Ref<Image>();
+ }
+
+ return frames[current_frame].texture->get_data();
+}
+
+void AnimatedTexture::set_flags(uint32_t p_flags) {
+}
+uint32_t AnimatedTexture::get_flags() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 0;
+ }
+
+ return frames[current_frame].texture->get_flags();
+}
+
+void AnimatedTexture::_validate_property(PropertyInfo &property) const {
+
+ String prop = property.name;
+ if (prop.begins_with("frame_")) {
+ int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ if (frame >= frame_count) {
+ property.usage = 0;
+ }
+ }
+}
+
+void AnimatedTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames);
+ ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames);
+
+ ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps);
+ ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps);
+
+ ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture);
+ ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture);
+
+ ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay);
+ ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay);
+
+ ClassDB::bind_method(D_METHOD("_update_proxy"), &AnimatedTexture::_update_proxy);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps");
+
+ for (int i = 0; i < MAX_FRAMES; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_frame_texture", "get_frame_texture", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_frame_delay", "get_frame_delay", i);
+ }
+}
+
+AnimatedTexture::AnimatedTexture() {
+ proxy = VS::get_singleton()->texture_create();
+ VisualServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true);
+ time = 0;
+ frame_count = 1;
+ fps = 4;
+ prev_ticks = 0;
+ current_frame = 0;
+ VisualServer::get_singleton()->connect("frame_pre_draw", this, "_update_proxy");
+}
+
+AnimatedTexture::~AnimatedTexture() {
+ VS::get_singleton()->free(proxy);
+}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index d81fd3b19b..c994bdad5f 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -34,10 +34,11 @@
#include "curve.h"
#include "io/resource_loader.h"
#include "math_2d.h"
+#include "os/mutex.h"
+#include "os/thread_safe.h"
#include "resource.h"
#include "scene/resources/color_ramp.h"
#include "servers/visual_server.h"
-
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -521,4 +522,70 @@ public:
~ProxyTexture();
};
+class AnimatedTexture : public Texture {
+ GDCLASS(AnimatedTexture, Texture)
+
+ _THREAD_SAFE_CLASS_
+
+private:
+ enum {
+ MAX_FRAMES = 256
+ };
+
+ RID proxy;
+
+ struct Frame {
+
+ Ref<Texture> texture;
+ float delay_sec;
+
+ Frame() {
+ delay_sec = 0;
+ }
+ };
+
+ Frame frames[MAX_FRAMES];
+ int frame_count;
+ int current_frame;
+
+ float fps;
+
+ float time;
+
+ uint64_t prev_ticks;
+
+ void _update_proxy();
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+public:
+ void set_frames(int p_frames);
+ int get_frames() const;
+
+ void set_frame_texture(int p_frame, const Ref<Texture> &p_texture);
+ Ref<Texture> get_frame_texture(int p_frame) const;
+
+ void set_frame_delay(int p_frame, float p_delay_sec);
+ float get_frame_delay(int p_frame) const;
+
+ void set_fps(float p_fps);
+ float get_fps() const;
+
+ 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;
+
+ AnimatedTexture();
+ ~AnimatedTexture();
+};
+
#endif
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index c23f237c75..e0d4038e7e 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -55,12 +55,12 @@ class Theme : public Resource {
void _unref_font(Ref<Font> p_sc);
void _emit_theme_changed();
- HashMap<StringName, HashMap<StringName, Ref<Texture>, StringNameHasher>, StringNameHasher> icon_map;
- HashMap<StringName, HashMap<StringName, Ref<StyleBox>, StringNameHasher>, StringNameHasher> style_map;
- HashMap<StringName, HashMap<StringName, Ref<Font>, StringNameHasher>, StringNameHasher> font_map;
- HashMap<StringName, HashMap<StringName, Ref<Shader>, StringNameHasher>, StringNameHasher> shader_map;
- HashMap<StringName, HashMap<StringName, Color, StringNameHasher>, StringNameHasher> color_map;
- HashMap<StringName, HashMap<StringName, int, StringNameHasher>, StringNameHasher> constant_map;
+ HashMap<StringName, HashMap<StringName, Ref<Texture> > > icon_map;
+ HashMap<StringName, HashMap<StringName, Ref<StyleBox> > > style_map;
+ HashMap<StringName, HashMap<StringName, Ref<Font> > > font_map;
+ HashMap<StringName, HashMap<StringName, Ref<Shader> > > shader_map;
+ HashMap<StringName, HashMap<StringName, Color> > color_map;
+ HashMap<StringName, HashMap<StringName, int> > constant_map;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 58057cda0c..f9df6b4304 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -923,6 +923,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("tile_get_normal_map", "id"), &TileSet::tile_get_normal_map);
ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material);
ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material);
+ ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate);
+ ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate);
ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset);
ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset);
ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
new file mode 100644
index 0000000000..b7b7802d1b
--- /dev/null
+++ b/scene/resources/visual_shader.cpp
@@ -0,0 +1,1555 @@
+#include "visual_shader.h"
+#include "servers/visual/shader_types.h"
+#include "vmap.h"
+
+void VisualShaderNode::set_output_port_for_preview(int p_index) {
+
+ port_preview = p_index;
+}
+
+int VisualShaderNode::get_output_port_for_preview() const {
+
+ return port_preview;
+}
+
+void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value) {
+ default_input_values[p_port] = p_value;
+ emit_changed();
+}
+
+Variant VisualShaderNode::get_input_port_default_value(int p_port) const {
+ if (default_input_values.has(p_port)) {
+ return default_input_values[p_port];
+ }
+
+ return Variant();
+}
+
+bool VisualShaderNode::is_port_separator(int p_index) const {
+ return false;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ return Vector<VisualShader::DefaultTextureParam>();
+}
+String VisualShaderNode::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return String();
+}
+
+Vector<StringName> VisualShaderNode::get_editable_properties() const {
+ return Vector<StringName>();
+}
+
+Array VisualShaderNode::_get_default_input_values() const {
+
+ Array ret;
+ for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ ret.push_back(E->get());
+ }
+ return ret;
+}
+void VisualShaderNode::_set_default_input_values(const Array &p_values) {
+
+ if (p_values.size() % 2 == 0) {
+ for (int i = 0; i < p_values.size(); i += 2) {
+ default_input_values[p_values[i + 0]] = p_values[i + 1];
+ }
+ }
+
+ emit_changed();
+}
+
+String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ return String();
+}
+
+void VisualShaderNode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview);
+ ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview);
+
+ ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
+
+ ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualShaderNode::_set_default_input_values);
+ ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualShaderNode::_get_default_input_values);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_default_input_values", "_get_default_input_values");
+ ADD_SIGNAL(MethodInfo("editor_refresh_request"));
+}
+
+VisualShaderNode::VisualShaderNode() {
+ port_preview = -1;
+}
+
+/////////////////////////////////////////////////////////
+
+void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) {
+ ERR_FAIL_COND(p_node.is_null());
+ ERR_FAIL_COND(p_id < 2);
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(g->nodes.has(p_id));
+ Node n;
+ n.node = p_node;
+ n.position = p_position;
+
+ Ref<VisualShaderNodeUniform> uniform = n.node;
+ if (uniform.is_valid()) {
+ String valid_name = validate_uniform_name(uniform->get_uniform_name(), uniform);
+ uniform->set_uniform_name(valid_name);
+ }
+
+ Ref<VisualShaderNodeInput> input = n.node;
+ if (input.is_valid()) {
+ input->shader_mode = shader_mode;
+ input->shader_type = p_type;
+ input->connect("input_type_changed", this, "_input_type_changed", varray(p_type, p_id));
+ }
+
+ n.node->connect("changed", this, "_queue_update");
+
+ g->nodes[p_id] = n;
+
+ _queue_update();
+}
+
+void VisualShader::set_node_position(Type p_type, int p_id, const Vector2 &p_position) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(!g->nodes.has(p_id));
+ g->nodes[p_id].position = p_position;
+}
+
+Vector2 VisualShader::get_node_position(Type p_type, int p_id) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector2());
+ const Graph *g = &graph[p_type];
+ 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];
+ ERR_FAIL_COND_V(!g->nodes.has(p_id), Ref<VisualShaderNode>());
+ return g->nodes[p_id].node;
+}
+
+Vector<int> VisualShader::get_node_list(Type p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector<int>());
+ const Graph *g = &graph[p_type];
+
+ Vector<int> ret;
+ for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+
+ return ret;
+}
+int VisualShader::get_valid_node_id(Type p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, NODE_ID_INVALID);
+ const Graph *g = &graph[p_type];
+ return g->nodes.size() ? MAX(2, g->nodes.back()->key() + 1) : 2;
+}
+
+int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const {
+ for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) {
+ if (E->get().node == p_node)
+ return E->key();
+ }
+
+ return NODE_ID_INVALID;
+}
+
+void VisualShader::remove_node(Type p_type, int p_id) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ ERR_FAIL_COND(p_id < 2);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(!g->nodes.has(p_id));
+
+ Ref<VisualShaderNodeInput> input = g->nodes[p_id].node;
+ if (input.is_valid()) {
+ input->disconnect("input_type_changed", this, "_input_type_changed");
+ }
+
+ g->nodes[p_id].node->disconnect("changed", this, "_queue_update");
+
+ g->nodes.erase(p_id);
+
+ for (List<Connection>::Element *E = g->connections.front(); E;) {
+ List<Connection>::Element *N = E->next();
+ if (E->get().from_node == p_id || E->get().to_node == p_id) {
+ g->connections.erase(E);
+ }
+ E = N;
+ }
+
+ _queue_update();
+}
+
+bool VisualShader::is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false);
+ const Graph *g = &graph[p_type];
+
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
+
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false);
+ const Graph *g = &graph[p_type];
+
+ if (!g->nodes.has(p_from_node))
+ return false;
+
+ if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count())
+ return false;
+
+ if (!g->nodes.has(p_to_node))
+ return false;
+
+ if (p_to_port < 0 || p_to_port >= g->nodes[p_to_node].node->get_input_port_count())
+ return false;
+
+ 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))) {
+ return false;
+ }
+
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+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];
+
+ ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER);
+
+ 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)
+ return ERR_INVALID_PARAMETER;
+ }
+
+ for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ ERR_FAIL_V(ERR_ALREADY_EXISTS);
+ }
+ }
+
+ 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();
+ 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];
+
+ for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ g->connections.erase(E);
+ _queue_update();
+ return;
+ }
+ }
+}
+
+Array VisualShader::_get_node_connections(Type p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Array());
+ const Graph *g = &graph[p_type];
+
+ Array ret;
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+ Dictionary d;
+ d["from_node"] = E->get().from_node;
+ d["from_port"] = E->get().from_port;
+ d["to_node"] = E->get().to_node;
+ d["to_port"] = E->get().to_port;
+ ret.push_back(d);
+ }
+
+ 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];
+
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+ r_connections->push_back(E->get());
+ }
+}
+
+void VisualShader::set_mode(Mode p_mode) {
+ if (shader_mode == p_mode) {
+ return;
+ }
+
+ //erase input/output connections
+ modes.clear();
+ flags.clear();
+ shader_mode = p_mode;
+ for (int i = 0; i < TYPE_MAX; i++) {
+
+ for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+
+ Ref<VisualShaderNodeInput> input = E->get().node;
+ if (input.is_valid()) {
+ input->shader_mode = shader_mode;
+ //input->input_index = 0;
+ }
+ }
+
+ Ref<VisualShaderNodeOutput> output = graph[i].nodes[NODE_ID_OUTPUT].node;
+ output->shader_mode = shader_mode;
+
+ // clear connections since they are no longer valid
+ for (List<Connection>::Element *E = graph[i].connections.front(); E;) {
+
+ bool keep = true;
+
+ List<Connection>::Element *N = E->next();
+
+ int from = E->get().from_node;
+ int to = E->get().to_node;
+
+ if (!graph[i].nodes.has(from)) {
+ keep = false;
+ } else {
+ Ref<VisualShaderNode> from_node = graph[i].nodes[from].node;
+ if (from_node->is_class("VisualShaderNodeOutput") || from_node->is_class("VisualShaderNodeInput")) {
+ keep = false;
+ }
+ }
+
+ if (!graph[i].nodes.has(to)) {
+ keep = false;
+ } else {
+ Ref<VisualShaderNode> to_node = graph[i].nodes[to].node;
+ if (to_node->is_class("VisualShaderNodeOutput") || to_node->is_class("VisualShaderNodeInput")) {
+ keep = false;
+ }
+ }
+
+ if (!keep) {
+ graph[i].connections.erase(E);
+ }
+ E = N;
+ }
+ }
+
+ _queue_update();
+ _change_notify();
+}
+
+void VisualShader::set_graph_offset(const Vector2 &p_offset) {
+ graph_offset = p_offset;
+}
+
+Vector2 VisualShader::get_graph_offset() const {
+ return graph_offset;
+}
+
+Shader::Mode VisualShader::get_mode() const {
+ return shader_mode;
+}
+
+String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const {
+
+ Ref<VisualShaderNode> node = get_node(p_type, p_node);
+ ERR_FAIL_COND_V(!node.is_valid(), String());
+ ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String());
+ ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String());
+
+ StringBuilder global_code;
+ StringBuilder code;
+
+ global_code += String() + "shader_type canvas_item;\n";
+
+ //make it faster to go around through shader
+ VMap<ConnectionKey, const List<Connection>::Element *> input_connections;
+ VMap<ConnectionKey, const List<Connection>::Element *> output_connections;
+
+ for (const List<Connection>::Element *E = graph[p_type].connections.front(); E; E = E->next()) {
+ ConnectionKey from_key;
+ from_key.node = E->get().from_node;
+ from_key.port = E->get().from_port;
+
+ output_connections.insert(from_key, E);
+
+ ConnectionKey to_key;
+ to_key.node = E->get().to_node;
+ to_key.port = E->get().to_port;
+
+ input_connections.insert(to_key, E);
+ }
+
+ code += "\nvoid fragment() {\n";
+
+ Set<int> processed;
+ Error err = _write_node(p_type, global_code, code, default_tex_params, input_connections, output_connections, p_node, processed, true);
+ ERR_FAIL_COND_V(err != OK, String());
+
+ 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 {
+ code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ }
+ code += "}\n";
+
+ //set code secretly
+ global_code += "\n\n";
+ String final_code = global_code;
+ final_code += code;
+ //print_line(final_code);
+ return final_code;
+}
+
+#define IS_INITIAL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z'))
+
+#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_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const {
+
+ String name = p_name; //validate name first
+ 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;
+ }
+
+ if (name == String()) {
+ name = p_uniform->get_caption();
+ }
+
+ int attempt = 1;
+
+ while (true) {
+
+ bool exists = false;
+ for (int i = 0; i < TYPE_MAX; i++) {
+ for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+ Ref<VisualShaderNodeUniform> node = E->get().node;
+ if (node == p_uniform) { //do not test on self
+ continue;
+ }
+ if (node.is_valid() && node->get_uniform_name() == name) {
+ exists = true;
+ break;
+ }
+ }
+ if (exists) {
+ break;
+ }
+ }
+
+ if (exists) {
+ //remove numbers, put new and try again
+ attempt++;
+ while (name.length() && name[name.length() - 1] >= '0' && name[name.length() - 1] <= '9') {
+ name = name.substr(0, name.length() - 1);
+ }
+ ERR_FAIL_COND_V(name == String(), String());
+ name += itos(attempt);
+ } else {
+ break;
+ }
+ }
+
+ return name;
+}
+
+VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = {
+ { Shader::MODE_SPATIAL, "blend" },
+ { Shader::MODE_SPATIAL, "depth_draw" },
+ { Shader::MODE_SPATIAL, "cull" },
+ { Shader::MODE_SPATIAL, "diffuse" },
+ { Shader::MODE_SPATIAL, "specular" },
+ { Shader::MODE_CANVAS_ITEM, "blend" },
+ { Shader::MODE_CANVAS_ITEM, NULL }
+};
+
+static const char *type_string[VisualShader::TYPE_MAX] = {
+ "vertex",
+ "fragment",
+ "light"
+};
+bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
+
+ String name = p_name;
+ if (name == "mode") {
+ set_mode(Shader::Mode(int(p_value)));
+ return true;
+ } else if (name.begins_with("flags/")) {
+ StringName flag = name.get_slicec('/', 1);
+ bool enable = p_value;
+ if (enable) {
+ flags.insert(flag);
+ } else {
+ flags.erase(flag);
+ }
+ _queue_update();
+ return true;
+ } else if (name.begins_with("modes/")) {
+ String mode = name.get_slicec('/', 1);
+ int value = p_value;
+ if (value == 0) {
+ modes.erase(mode); //means its default anyway, so dont store it
+ } else {
+ modes[mode] = value;
+ }
+ _queue_update();
+ return true;
+ } else if (name.begins_with("nodes/")) {
+ String typestr = name.get_slicec('/', 1);
+ Type type = TYPE_VERTEX;
+ for (int i = 0; i < TYPE_MAX; i++) {
+ if (typestr == type_string[i]) {
+ type = Type(i);
+ break;
+ }
+ }
+
+ String index = name.get_slicec('/', 2);
+ if (index == "connections") {
+
+ 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]);
+ }
+ }
+ return true;
+ }
+
+ int id = index.to_int();
+ String what = name.get_slicec('/', 3);
+
+ if (what == "node") {
+ add_node(type, p_value, Vector2(), id);
+ return true;
+ } else if (what == "position") {
+ set_node_position(type, id, p_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String name = p_name;
+ if (name == "mode") {
+ r_ret = get_mode();
+ return true;
+ } else if (name.begins_with("flags/")) {
+ StringName flag = name.get_slicec('/', 1);
+ r_ret = flags.has(flag);
+ return true;
+ } else if (name.begins_with("modes/")) {
+ String mode = name.get_slicec('/', 1);
+ if (modes.has(mode)) {
+ r_ret = modes[mode];
+ } else {
+ r_ret = 0;
+ }
+ return true;
+ } else if (name.begins_with("nodes/")) {
+ String typestr = name.get_slicec('/', 1);
+ Type type = TYPE_VERTEX;
+ for (int i = 0; i < TYPE_MAX; i++) {
+ if (typestr == type_string[i]) {
+ type = Type(i);
+ break;
+ }
+ }
+
+ String index = name.get_slicec('/', 2);
+ if (index == "connections") {
+
+ Vector<int> conns;
+ for (const List<Connection>::Element *E = graph[type].connections.front(); E; E = E->next()) {
+ conns.push_back(E->get().from_node);
+ conns.push_back(E->get().from_port);
+ conns.push_back(E->get().to_node);
+ conns.push_back(E->get().to_port);
+ }
+
+ r_ret = conns;
+ return true;
+ }
+
+ int id = index.to_int();
+ String what = name.get_slicec('/', 3);
+
+ if (what == "node") {
+ r_ret = get_node(type, id);
+ return true;
+ } else if (what == "position") {
+ r_ret = get_node_position(type, id);
+ return true;
+ }
+ }
+ return false;
+}
+void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ //mode
+ p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles"));
+ //render modes
+
+ Map<String, String> blend_mode_enums;
+ Set<String> toggles;
+
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) {
+
+ String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i];
+ int idx = 0;
+ bool in_enum = false;
+ while (render_mode_enums[idx].string) {
+ if (mode.begins_with(render_mode_enums[idx].string)) {
+ String begin = render_mode_enums[idx].string;
+ String option = mode.replace_first(begin + "_", "");
+ if (!blend_mode_enums.has(begin)) {
+ blend_mode_enums[begin] = option;
+ } else {
+ blend_mode_enums[begin] += "," + option;
+ }
+ in_enum = true;
+ break;
+ }
+ idx++;
+ }
+
+ if (!in_enum) {
+ toggles.insert(mode);
+ }
+ }
+
+ for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) {
+
+ p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get()));
+ }
+
+ for (Set<String>::Element *E = toggles.front(); E; E = E->next()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get()));
+ }
+
+ for (int i = 0; i < TYPE_MAX; i++) {
+ for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+
+ String prop_name = "nodes/";
+ prop_name += type_string[i];
+ prop_name += "/" + itos(E->key());
+
+ if (E->key() != NODE_ID_OUTPUT) {
+
+ 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));
+ }
+ p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+}
+
+Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const {
+
+ const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node;
+
+ //check inputs recursively first
+ int input_count = vsnode->get_input_port_count();
+ for (int i = 0; i < input_count; i++) {
+ ConnectionKey ck;
+ ck.node = node;
+ ck.port = i;
+
+ if (input_connections.has(ck)) {
+ int from_node = input_connections[ck]->get().from_node;
+ if (processed.has(from_node)) {
+ continue;
+ }
+
+ Error err = _write_node(type, global_code, code, def_tex_params, input_connections, output_connections, from_node, processed, for_preview);
+ if (err)
+ return err;
+ }
+ }
+
+ // then this node
+
+ code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ Vector<String> input_vars;
+
+ input_vars.resize(vsnode->get_input_port_count());
+ String *inputs = input_vars.ptrw();
+
+ for (int i = 0; i < input_count; i++) {
+ ConnectionKey ck;
+ ck.node = node;
+ ck.port = i;
+
+ if (input_connections.has(ck)) {
+ //connected to something, use that output
+ int from_node = input_connections[ck]->get().from_node;
+ int from_port = input_connections[ck]->get().from_port;
+
+ VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i);
+ VisualShaderNode::PortType out_type = graph[type].nodes[from_node].node->get_output_port_type(from_port);
+
+ String src_var = "n_out" + itos(from_node) + "p" + itos(from_port);
+
+ if (in_type == out_type) {
+ inputs[i] = src_var;
+ } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
+ 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 {
+
+ Variant defval = vsnode->get_input_port_default_value(i);
+ if (defval.get_type() == Variant::REAL || defval.get_type() == Variant::INT) {
+ 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::VECTOR3) {
+ Vector3 val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f,%.5f,%.5f);\n", val.x, val.y, val.z);
+ } else if (defval.get_type() == Variant::TRANSFORM) {
+ Transform val = defval;
+ val.basis.transpose();
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ Array values;
+ for (int i = 0; i < 3; i++) {
+ values.push_back(val.basis[i].x);
+ values.push_back(val.basis[i].y);
+ values.push_back(val.basis[i].z);
+ }
+ values.push_back(val.origin.x);
+ values.push_back(val.origin.y);
+ values.push_back(val.origin.z);
+ bool err = false;
+ code += "\tmat4 " + inputs[i] + " = " + String("mat4( vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,1.0) );\n").sprintf(values, &err);
+ } else {
+ //will go empty, node is expected to know what it is doing at this point and handle it
+ }
+ }
+ }
+
+ int output_count = vsnode->get_output_port_count();
+ Vector<String> output_vars;
+ output_vars.resize(vsnode->get_output_port_count());
+ String *outputs = output_vars.ptrw();
+
+ for (int i = 0; i < output_count; i++) {
+
+ outputs[i] = "n_out" + itos(node) + "p" + itos(i);
+ 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_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break;
+ default: {}
+ }
+ }
+
+ Vector<VisualShader::DefaultTextureParam> params = vsnode->get_default_texture_parameters(type, node);
+ for (int i = 0; i < params.size(); i++) {
+ def_tex_params.push_back(params[i]);
+ }
+
+ Ref<VisualShaderNodeInput> input = vsnode;
+
+ if (input.is_valid() && for_preview) {
+ //handle for preview
+ code += input->generate_code_for_preview(type, node, inputs, outputs);
+ } else {
+ //handle normally
+ global_code += vsnode->generate_global(get_mode(), type, node);
+ code += vsnode->generate_code(get_mode(), type, node, inputs, outputs);
+ }
+ code += "\n"; //
+ processed.insert(node);
+
+ return OK;
+}
+
+void VisualShader::_update_shader() const {
+ if (!dirty)
+ return;
+
+ dirty = false;
+
+ StringBuilder global_code;
+ StringBuilder code;
+ Vector<VisualShader::DefaultTextureParam> default_tex_params;
+ static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles" };
+
+ global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n";
+
+ String render_mode;
+
+ {
+ //fill render mode enums
+ int idx = 0;
+ while (render_mode_enums[idx].string) {
+
+ if (shader_mode == render_mode_enums[idx].mode) {
+
+ if (modes.has(render_mode_enums[idx].string)) {
+
+ int which = modes[render_mode_enums[idx].string];
+ int count = 0;
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) {
+ String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i];
+ if (mode.begins_with(render_mode_enums[idx].string)) {
+ if (count == which) {
+ if (render_mode != String()) {
+ render_mode += ", ";
+ }
+ render_mode += mode;
+ break;
+ }
+ count++;
+ }
+ }
+ }
+ }
+ idx++;
+ }
+
+ //fill render mode flags
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) {
+
+ String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i];
+ if (flags.has(mode)) {
+ if (render_mode != String()) {
+ render_mode += ", ";
+ }
+ render_mode += mode;
+ }
+ }
+ }
+
+ if (render_mode != String()) {
+
+ global_code += "render_mode " + render_mode + ";\n\n";
+ }
+
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light" };
+
+ for (int i = 0; i < TYPE_MAX; i++) {
+
+ //make it faster to go around through shader
+ VMap<ConnectionKey, const List<Connection>::Element *> input_connections;
+ VMap<ConnectionKey, const List<Connection>::Element *> output_connections;
+
+ for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) {
+ ConnectionKey from_key;
+ from_key.node = E->get().from_node;
+ from_key.port = E->get().from_port;
+
+ output_connections.insert(from_key, E);
+
+ ConnectionKey to_key;
+ to_key.node = E->get().to_node;
+ to_key.port = E->get().to_port;
+
+ input_connections.insert(to_key, E);
+ }
+
+ code += "\nvoid " + String(func_name[i]) + "() {\n";
+
+ Set<int> processed;
+ Error err = _write_node(Type(i), global_code, code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false);
+ ERR_FAIL_COND(err != OK);
+
+ code += "}\n";
+ }
+
+ //set code secretly
+ global_code += "\n\n";
+ String final_code = global_code;
+ final_code += code;
+ const_cast<VisualShader *>(this)->set_code(final_code);
+ //print_line(final_code);
+ for (int i = 0; i < default_tex_params.size(); i++) {
+ const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param);
+ }
+}
+
+void VisualShader::_queue_update() {
+ if (dirty) {
+ return;
+ }
+
+ dirty = true;
+ call_deferred("_update_shader");
+}
+
+void VisualShader::_input_type_changed(Type p_type, int p_id) {
+ //erase connections using this input, as type changed
+ Graph *g = &graph[p_type];
+
+ for (List<Connection>::Element *E = g->connections.front(); E;) {
+ List<Connection>::Element *N = E->next();
+ if (E->get().from_node == p_id) {
+ g->connections.erase(E);
+ }
+ E = N;
+ }
+}
+
+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_position", "type", "id"), &VisualShader::get_node_position);
+ ClassDB::bind_method(D_METHOD("get_node", "type"), &VisualShader::get_node);
+
+ 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("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("get_node_connections", "type"), &VisualShader::_get_node_connections);
+
+ ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset);
+ ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset);
+
+ ClassDB::bind_method(D_METHOD("_queue_update"), &VisualShader::_queue_update);
+ ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
+
+ ClassDB::bind_method(D_METHOD("_input_type_changed"), &VisualShader::_input_type_changed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
+
+ BIND_ENUM_CONSTANT(TYPE_VERTEX);
+ BIND_ENUM_CONSTANT(TYPE_FRAGMENT);
+ BIND_ENUM_CONSTANT(TYPE_LIGHT);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
+
+ BIND_CONSTANT(NODE_ID_INVALID);
+ BIND_CONSTANT(NODE_ID_OUTPUT);
+}
+
+VisualShader::VisualShader() {
+ shader_mode = Shader::MODE_SPATIAL;
+
+ for (int i = 0; i < TYPE_MAX; i++) {
+ Ref<VisualShaderNodeOutput> output;
+ output.instance();
+ output->shader_type = Type(i);
+ output->shader_mode = shader_mode;
+ graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150);
+ }
+
+ dirty = true;
+}
+
+///////////////////////////////////////////////////////////
+
+const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
+ // Spatial, Vertex
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0)" },
+
+ // Spatial, Fragment
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+
+ // Spatial, Light
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "attenuation", "ATTENUATION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+ // Canvas Item, Vertex
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "extra", "EXTRA_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_alpha", "LIGHT_COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" },
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Vertex
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "index", "float(INDEX)" },
+
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL },
+};
+
+const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
+
+ // Spatial, Fragment
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0,1.0,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0,0.0,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0,1.0, 0.0)" },
+
+ // Spatial, Light
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0, 1.0, 0.0)" },
+ // Canvas Item, Vertex
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Vertex
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL },
+};
+
+int VisualShaderNodeInput::get_input_port_count() const {
+
+ return 0;
+}
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_port_type(int p_port) const {
+
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeInput::get_input_port_name(int p_port) const {
+
+ return "";
+}
+
+int VisualShaderNodeInput::get_output_port_count() const {
+
+ return 1;
+}
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const {
+
+ return get_input_type_by_name(input_name);
+}
+String VisualShaderNodeInput::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeInput::get_caption() const {
+ return TTR("Input");
+}
+
+String VisualShaderNodeInput::generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ int idx = 0;
+
+ String code;
+
+ while (preview_ports[idx].mode != Shader::MODE_MAX) {
+ if (preview_ports[idx].mode == shader_mode && preview_ports[idx].shader_type == shader_type && preview_ports[idx].name == input_name) {
+ code = "\t" + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n";
+ break;
+ }
+ idx++;
+ }
+
+ if (code == String()) {
+ switch (get_output_port_type(0)) {
+ case PORT_TYPE_SCALAR: {
+ code = "\t" + p_output_vars[0] + " = 0.0;\n";
+ } break; //default (none found) is scalar
+ case PORT_TYPE_VECTOR: {
+ code = "\t" + p_output_vars[0] + " = vec3(0.0);\n";
+ } break; //default (none found) is scalar
+ 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
+ }
+ }
+
+ return code;
+}
+
+String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ int idx = 0;
+
+ String code;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) {
+ code = "\t" + p_output_vars[0] + " = " + ports[idx].string + ";\n";
+ break;
+ }
+ idx++;
+ }
+
+ if (code == String()) {
+ code = "\t" + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar
+ }
+
+ return code;
+}
+
+void VisualShaderNodeInput::set_input_name(String p_name) {
+ PortType prev_type = get_input_type_by_name(input_name);
+ input_name = p_name;
+ emit_changed();
+ if (get_input_type_by_name(input_name) != prev_type) {
+ emit_signal("input_type_changed");
+ }
+}
+
+String VisualShaderNodeInput::get_input_name() const {
+ return input_name;
+}
+
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_type_by_name(String p_name) const {
+
+ int idx = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == p_name) {
+ return ports[idx].type;
+ }
+ idx++;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+int VisualShaderNodeInput::get_input_index_count() const {
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ count++;
+ }
+ idx++;
+ }
+
+ return count;
+}
+
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_index_type(int p_index) const {
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_index) {
+ return ports[idx].type;
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeInput::get_input_index_name(int p_index) const {
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_index) {
+ return ports[idx].name;
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return "";
+}
+
+void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "input_name") {
+ String port_list;
+
+ int idx = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (port_list != String()) {
+ port_list += ",";
+ }
+ port_list += ports[idx].name;
+ }
+ idx++;
+ }
+
+ if (port_list == "") {
+ port_list = TTR("None");
+ }
+ property.hint_string = port_list;
+ }
+}
+
+Vector<StringName> VisualShaderNodeInput::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("input_name");
+ return props;
+}
+
+void VisualShaderNodeInput::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name);
+ ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_name", PROPERTY_HINT_ENUM, ""), "set_input_name", "get_input_name");
+ ADD_SIGNAL(MethodInfo("input_type_changed"));
+}
+VisualShaderNodeInput::VisualShaderNodeInput() {
+ input_name = "[None]";
+ // changed when set
+ shader_type = VisualShader::TYPE_MAX;
+ shader_mode = Shader::MODE_MAX;
+}
+
+////////////////////////////////////////////
+
+const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
+ // Spatial, Vertex
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "UV2:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+ // Spatial, Fragment
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor", "ALPHA_SCISSOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" },
+
+ // Spatial, Light
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
+ // Canvas Item, Vertex
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" },
+ // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.rgb" },
+ // Particles, Vertex
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL },
+};
+
+int VisualShaderNodeOutput::get_input_port_count() const {
+
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ count++;
+ }
+ idx++;
+ }
+
+ return count;
+}
+
+VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_input_port_type(int p_port) const {
+
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_port) {
+ return ports[idx].type;
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeOutput::get_input_port_name(int p_port) const {
+
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_port) {
+ return String(ports[idx].name).capitalize();
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return String();
+}
+
+Variant VisualShaderNodeOutput::get_input_port_default_value(int p_port) const {
+ return Variant();
+}
+
+int VisualShaderNodeOutput::get_output_port_count() const {
+
+ return 0;
+}
+VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeOutput::get_output_port_name(int p_port) const {
+ return String();
+}
+
+bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
+
+ if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
+ String name = get_input_port_name(p_index);
+ return (name == "Normal" || name == "Rim" || name == "Alpha Scissor");
+ }
+ return false;
+}
+
+String VisualShaderNodeOutput::get_caption() const {
+ return TTR("Output");
+}
+
+String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ int idx = 0;
+ int count = 0;
+
+ String code;
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+
+ if (p_input_vars[count] != String()) {
+ String s = ports[idx].string;
+ if (s.find(":") != -1) {
+ code += "\t" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
+ } else {
+ code += "\t" + s + " = " + p_input_vars[count] + ";\n";
+ }
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return code;
+}
+
+VisualShaderNodeOutput::VisualShaderNodeOutput() {
+}
+
+///////////////////////////
+
+void VisualShaderNodeUniform::set_uniform_name(const String &p_name) {
+ uniform_name = p_name;
+ emit_signal("name_changed");
+ emit_changed();
+}
+
+String VisualShaderNodeUniform::get_uniform_name() const {
+ return uniform_name;
+}
+
+void VisualShaderNodeUniform::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniform::set_uniform_name);
+ ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniform::get_uniform_name);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "uniform_name"), "set_uniform_name", "get_uniform_name");
+}
+
+VisualShaderNodeUniform::VisualShaderNodeUniform() {
+}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
new file mode 100644
index 0000000000..6ff1c9a9fe
--- /dev/null
+++ b/scene/resources/visual_shader.h
@@ -0,0 +1,284 @@
+#ifndef VISUAL_SHADER_H
+#define VISUAL_SHADER_H
+
+#include "scene/resources/shader.h"
+#include "string_builder.h"
+
+class VisualShaderNodeUniform;
+class VisualShaderNode;
+
+class VisualShader : public Shader {
+ GDCLASS(VisualShader, Shader)
+public:
+ enum Type {
+ TYPE_VERTEX,
+ TYPE_FRAGMENT,
+ TYPE_LIGHT,
+ TYPE_MAX
+ };
+
+ struct Connection {
+ int from_node;
+ int from_port;
+ int to_node;
+ int to_port;
+ };
+
+ struct DefaultTextureParam {
+ StringName name;
+ Ref<Texture> param;
+ };
+
+private:
+ struct Node {
+ Ref<VisualShaderNode> node;
+ Vector2 position;
+ };
+
+ struct Graph {
+ Map<int, Node> nodes;
+ List<Connection> connections;
+ } graph[TYPE_MAX];
+
+ Shader::Mode shader_mode;
+
+ Array _get_node_connections(Type p_type) const;
+
+ Vector2 graph_offset;
+
+ struct RenderModeEnums {
+ Shader::Mode mode;
+ const char *string;
+ };
+
+ HashMap<String, int> modes;
+ Set<StringName> flags;
+
+ static RenderModeEnums render_mode_enums[];
+
+ volatile mutable bool dirty;
+ void _queue_update();
+
+ union ConnectionKey {
+
+ struct {
+ uint64_t node : 32;
+ uint64_t port : 32;
+ };
+ uint64_t key;
+ bool operator<(const ConnectionKey &p_key) const {
+ return key < p_key.key;
+ }
+ };
+
+ Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const;
+
+ void _input_type_changed(Type p_type, int p_id);
+
+protected:
+ virtual void _update_shader() const;
+ static void _bind_methods();
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ enum {
+ NODE_ID_INVALID = -1,
+ NODE_ID_OUTPUT = 0,
+ };
+
+ void add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id);
+ void set_node_position(Type p_type, int p_id, const Vector2 &p_position);
+
+ Vector2 get_node_position(Type p_type, int p_id) const;
+ Ref<VisualShaderNode> get_node(Type p_type, int p_id) const;
+
+ Vector<int> get_node_list(Type p_type) const;
+ int get_valid_node_id(Type p_type) const;
+
+ int find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const;
+ void remove_node(Type p_type, int p_id);
+
+ bool is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
+ 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 get_node_connections(Type p_type, List<Connection> *r_connections) const;
+
+ void set_mode(Mode p_mode);
+ virtual Mode get_mode() const;
+
+ void set_graph_offset(const Vector2 &p_offset);
+ Vector2 get_graph_offset() const;
+
+ String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
+
+ String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const;
+
+ VisualShader();
+};
+
+VARIANT_ENUM_CAST(VisualShader::Type)
+///
+///
+///
+
+class VisualShaderNode : public Resource {
+ GDCLASS(VisualShaderNode, Resource)
+
+ int port_preview;
+
+ Map<int, Variant> default_input_values;
+
+ Array _get_default_input_values() const;
+ void _set_default_input_values(const Array &p_values);
+
+protected:
+ static void _bind_methods();
+
+public:
+ enum PortType {
+ PORT_TYPE_SCALAR,
+ PORT_TYPE_VECTOR,
+ PORT_TYPE_TRANSFORM,
+ };
+
+ virtual String get_caption() const = 0;
+
+ virtual int get_input_port_count() const = 0;
+ virtual PortType get_input_port_type(int p_port) const = 0;
+ virtual String get_input_port_name(int p_port) const = 0;
+
+ void set_input_port_default_value(int p_port, const Variant &p_value);
+ Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
+
+ virtual int get_output_port_count() const = 0;
+ virtual PortType get_output_port_type(int p_port) const = 0;
+ virtual String get_output_port_name(int p_port) const = 0;
+
+ void set_output_port_for_preview(int p_index);
+ int get_output_port_for_preview() const;
+
+ virtual bool is_port_separator(int p_index) const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) 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) const = 0; //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
+
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const;
+
+ VisualShaderNode();
+};
+/////
+
+class VisualShaderNodeInput : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeInput, VisualShaderNode)
+
+ friend class VisualShader;
+ VisualShader::Type shader_type;
+ Shader::Mode shader_mode;
+
+ struct Port {
+ Shader::Mode mode;
+ VisualShader::Type shader_type;
+ PortType type;
+ const char *name;
+ const char *string;
+ };
+
+ static const Port ports[];
+ static const Port preview_ports[];
+
+ String input_name;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+public:
+ 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 get_caption() const;
+
+ virtual String generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) 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) const;
+
+ void set_input_name(String p_name);
+ String get_input_name() const;
+
+ int get_input_index_count() const;
+ PortType get_input_index_type(int p_index) const;
+ String get_input_index_name(int p_index) const;
+
+ PortType get_input_type_by_name(String p_name) const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeInput();
+};
+
+///
+
+class VisualShaderNodeOutput : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeOutput, VisualShaderNode)
+public:
+ friend class VisualShader;
+ VisualShader::Type shader_type;
+ Shader::Mode shader_mode;
+
+ struct Port {
+ Shader::Mode mode;
+ VisualShader::Type shader_type;
+ PortType type;
+ const char *name;
+ const char *string;
+ };
+
+ static const Port ports[];
+
+public:
+ 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;
+ Variant get_input_port_default_value(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 bool is_port_separator(int p_index) const;
+
+ virtual String get_caption() 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) const;
+
+ VisualShaderNodeOutput();
+};
+
+class VisualShaderNodeUniform : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeUniform, VisualShaderNode)
+
+ String uniform_name;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_uniform_name(const String &p_name);
+ String get_uniform_name() const;
+
+ VisualShaderNodeUniform();
+};
+
+#endif // VISUAL_SHADER_H
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
new file mode 100644
index 0000000000..98ecdbdf30
--- /dev/null
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -0,0 +1,1896 @@
+#include "visual_shader_nodes.h"
+////////////// Scalar
+
+String VisualShaderNodeScalarConstant::get_caption() const {
+ return "Scalar";
+}
+
+int VisualShaderNodeScalarConstant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeScalarConstant::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarConstant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n";
+}
+
+void VisualShaderNodeScalarConstant::set_constant(float p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+float VisualShaderNodeScalarConstant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeScalarConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeScalarConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeScalarConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeScalarConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeScalarConstant::VisualShaderNodeScalarConstant() {
+ constant = 0;
+}
+
+////////////// Color
+
+String VisualShaderNodeColorConstant::get_caption() const {
+ return "Color";
+}
+
+int VisualShaderNodeColorConstant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeColorConstant::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code;
+ code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.r, constant.g, constant.b) + ";\n";
+ code += "\t" + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n";
+
+ return code;
+}
+
+void VisualShaderNodeColorConstant::set_constant(Color p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+Color VisualShaderNodeColorConstant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeColorConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
+ constant = Color(1, 1, 1, 1);
+}
+
+////////////// Vector
+
+String VisualShaderNodeVec3Constant::get_caption() const {
+ return "Vector";
+}
+
+int VisualShaderNodeVec3Constant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec3Constant::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.x, constant.y, constant.z) + ";\n";
+}
+
+void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+Vector3 VisualShaderNodeVec3Constant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeVec3Constant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() {
+}
+
+////////////// Transform
+
+String VisualShaderNodeTransformConstant::get_caption() const {
+ return "Transform";
+}
+
+int VisualShaderNodeTransformConstant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeTransformConstant::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ Transform t = constant;
+ t.basis.transpose();
+
+ String code = "\t" + p_output_vars[0] + " = mat4(";
+ code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[0].x, t.basis[0].y, t.basis[0].z);
+ code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[1].x, t.basis[1].y, t.basis[1].z);
+ code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[2].x, t.basis[2].y, t.basis[2].z);
+ code += vformat("vec4(%.6f,%.6f,%.6f,1.0) );\n", t.origin.x, t.origin.y, t.origin.z);
+ return code;
+}
+
+void VisualShaderNodeTransformConstant::set_constant(Transform p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+Transform VisualShaderNodeTransformConstant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeTransformConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeTransformConstant::VisualShaderNodeTransformConstant() {
+}
+
+////////////// Texture
+
+String VisualShaderNodeTexture::get_caption() const {
+ return "Texture";
+}
+
+int VisualShaderNodeTexture::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTexture::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "uv" : "lod";
+}
+
+int VisualShaderNodeTexture::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
+
+ static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" };
+ return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "tex");
+ dtp.param = texture;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+
+ if (source == SOURCE_TEXTURE) {
+
+ String u = "uniform sampler2D " + make_unique_id(p_type, p_id, "tex");
+ switch (texture_type) {
+ case TYPE_DATA: break;
+ case TYPE_COLOR: u += " : hint_color"; break;
+ case TYPE_NORMALMAP: u += " : hint_normal"; break;
+ }
+ return u + ";";
+ }
+
+ return String();
+}
+
+String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ if (source == SOURCE_TEXTURE) {
+ String id = make_unique_id(p_type, p_id, "tex");
+ String code;
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\tvec4 " + id + "_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
+ return code;
+ }
+
+ if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 _tex_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy, 0.0 );\n";
+ } else {
+ code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+ }
+
+ if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 _tex_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 _tex_read = texture( TEXTURE , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\t\tvec4 _tex_read = textureLod( TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+ }
+
+ if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 _tex_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\t\tvec4 _tex_read = textureLod( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+ }
+
+ //none
+ String code;
+ code += "\t" + p_output_vars[0] + " = vec3(0.0);\n";
+ code += "\t" + p_output_vars[1] + " = 1.0;\n";
+ return code;
+}
+
+void VisualShaderNodeTexture::set_source(Source p_source) {
+ source = p_source;
+ emit_changed();
+ emit_signal("editor_refresh_request");
+}
+
+VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const {
+ return source;
+}
+
+void VisualShaderNodeTexture::set_texture(Ref<Texture> p_value) {
+
+ texture = p_value;
+ emit_changed();
+}
+
+Ref<Texture> VisualShaderNodeTexture::get_texture() const {
+
+ return texture;
+}
+
+void VisualShaderNodeTexture::set_texture_type(TextureType p_type) {
+ texture_type = p_type;
+ emit_changed();
+}
+
+VisualShaderNodeTexture::TextureType VisualShaderNodeTexture::get_texture_type() const {
+ return texture_type;
+}
+
+Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("source");
+ if (source == SOURCE_TEXTURE) {
+ props.push_back("texture");
+ props.push_back("texture_type");
+ }
+ return props;
+}
+
+String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+
+ if (source == SOURCE_TEXTURE) {
+ return String(); // all good
+ }
+
+ if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ return String(); // all good
+ }
+
+ if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ return String(); // all good
+ }
+
+ if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM) {
+
+ return String(); // all good
+ }
+
+ return TTR("Invalid source for shader.");
+}
+
+void VisualShaderNodeTexture::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeTexture::set_source);
+ ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeTexture::get_source);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "value"), &VisualShaderNodeTexture::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeTexture::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeTexture::set_texture_type);
+ ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTexture::get_texture_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D"), "set_source", "get_source");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+
+ BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
+ BIND_ENUM_CONSTANT(SOURCE_SCREEN);
+ BIND_ENUM_CONSTANT(SOURCE_2D_TEXTURE);
+ BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL);
+ BIND_ENUM_CONSTANT(TYPE_DATA);
+ BIND_ENUM_CONSTANT(TYPE_COLOR);
+ BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+}
+
+VisualShaderNodeTexture::VisualShaderNodeTexture() {
+ texture_type = TYPE_DATA;
+ source = SOURCE_TEXTURE;
+}
+
+////////////// CubeMap
+
+String VisualShaderNodeCubeMap::get_caption() const {
+ return "CubeMap";
+}
+
+int VisualShaderNodeCubeMap::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "uv" : "lod";
+}
+
+int VisualShaderNodeCubeMap::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMap::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubeMap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "cube");
+ dtp.param = cube_map;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+String VisualShaderNodeCubeMap::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+
+ String u = "uniform sampler2DCube " + make_unique_id(p_type, p_id, "cube");
+ switch (texture_type) {
+ case TYPE_DATA: break;
+ case TYPE_COLOR: u += " : hint_color"; break;
+ case TYPE_NORMALMAP: u += " : hint_normal"; break;
+ }
+ return u + ";";
+}
+
+String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String id = make_unique_id(p_type, p_id, "cube");
+ String code;
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\tvec4 " + id + "_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + " );\n";
+ } else {
+ code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
+ return code;
+}
+
+void VisualShaderNodeCubeMap::set_cube_map(Ref<CubeMap> p_value) {
+
+ cube_map = p_value;
+ emit_changed();
+}
+
+Ref<CubeMap> VisualShaderNodeCubeMap::get_cube_map() const {
+
+ return cube_map;
+}
+
+void VisualShaderNodeCubeMap::set_texture_type(TextureType p_type) {
+ texture_type = p_type;
+ emit_changed();
+}
+
+VisualShaderNodeCubeMap::TextureType VisualShaderNodeCubeMap::get_texture_type() const {
+ return texture_type;
+}
+
+Vector<StringName> VisualShaderNodeCubeMap::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("cube_map");
+ props.push_back("texture_type");
+ return props;
+}
+
+void VisualShaderNodeCubeMap::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_cube_map", "value"), &VisualShaderNodeCubeMap::set_cube_map);
+ ClassDB::bind_method(D_METHOD("get_cube_map"), &VisualShaderNodeCubeMap::get_cube_map);
+
+ ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeCubeMap::set_texture_type);
+ ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeCubeMap::get_texture_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "CubeMap"), "set_cube_map", "get_cube_map");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+
+ BIND_ENUM_CONSTANT(TYPE_DATA);
+ BIND_ENUM_CONSTANT(TYPE_COLOR);
+ BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+}
+
+VisualShaderNodeCubeMap::VisualShaderNodeCubeMap() {
+ texture_type = TYPE_DATA;
+}
+////////////// Scalar Op
+
+String VisualShaderNodeScalarOp::get_caption() const {
+ return "ScalarOp";
+}
+
+int VisualShaderNodeScalarOp::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarOp::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeScalarOp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarOp::get_output_port_name(int p_port) const {
+ return "op"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code = "\t" + p_output_vars[0] + " = ";
+ switch (op) {
+
+ case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break;
+ case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break;
+ case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break;
+ case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break;
+ case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ 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;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeScalarOp::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeScalarOp::Operator VisualShaderNodeScalarOp::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeScalarOp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+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");
+
+ BIND_ENUM_CONSTANT(OP_ADD);
+ BIND_ENUM_CONSTANT(OP_SUB);
+ BIND_ENUM_CONSTANT(OP_MUL);
+ BIND_ENUM_CONSTANT(OP_DIV);
+ BIND_ENUM_CONSTANT(OP_MOD);
+ BIND_ENUM_CONSTANT(OP_POW);
+ BIND_ENUM_CONSTANT(OP_MAX);
+ BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_ATAN2);
+}
+
+VisualShaderNodeScalarOp::VisualShaderNodeScalarOp() {
+ op = OP_ADD;
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+}
+
+////////////// Vector Op
+
+String VisualShaderNodeVectorOp::get_caption() const {
+ return "VectorOp";
+}
+
+int VisualShaderNodeVectorOp::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeVectorOp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const {
+ return "op"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code = "\t" + p_output_vars[0] + " = ";
+ switch (op) {
+
+ case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break;
+ case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break;
+ case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break;
+ case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break;
+ case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ 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;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeVectorOp::Operator VisualShaderNodeVectorOp::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+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");
+
+ BIND_ENUM_CONSTANT(OP_ADD);
+ BIND_ENUM_CONSTANT(OP_SUB);
+ BIND_ENUM_CONSTANT(OP_MUL);
+ BIND_ENUM_CONSTANT(OP_DIV);
+ BIND_ENUM_CONSTANT(OP_MOD);
+ BIND_ENUM_CONSTANT(OP_POW);
+ BIND_ENUM_CONSTANT(OP_MAX);
+ BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_CROSS);
+}
+
+VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
+ op = OP_ADD;
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Color Op
+
+String VisualShaderNodeColorOp::get_caption() const {
+ return "ColorOp";
+}
+
+int VisualShaderNodeColorOp::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorOp::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeColorOp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorOp::get_output_port_name(int p_port) const {
+ return "op"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code;
+ static const char *axisn[3] = { "x", "y", "z" };
+ switch (op) {
+ case OP_SCREEN: {
+
+ code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")*(vec3(1.0)-" + p_input_vars[1] + ");\n";
+ } break;
+ case OP_DIFFERENCE: {
+
+ code += "\t" + p_output_vars[0] + "=abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ");\n";
+ } break;
+ case OP_DARKEN: {
+
+ code += "\t" + p_output_vars[0] + "=min(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n";
+ } break;
+ case OP_LIGHTEN: {
+
+ code += "\t" + p_output_vars[0] + "=max(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n";
+
+ } break;
+ case OP_OVERLAY: {
+
+ for (int i = 0; i < 3; i++) {
+ code += "\t{\n";
+ code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += "\t\tif (base < 0.5) {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n";
+ code += "\t\t} else {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n";
+ code += "\t\t}\n";
+ code += "\t}\n";
+ }
+
+ } break;
+ case OP_DODGE: {
+
+ code += "\t" + p_output_vars[0] + "=(" + p_input_vars[0] + ")/(vec3(1.0)-" + p_input_vars[1] + ");\n";
+
+ } break;
+ case OP_BURN: {
+
+ code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")/(" + p_input_vars[1] + ");\n";
+ } break;
+ case OP_SOFT_LIGHT: {
+
+ for (int i = 0; i < 3; i++) {
+ code += "\t{\n";
+ code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += "\t\tif (base < 0.5) {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (blend+0.5));\n";
+ code += "\t\t} else {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-(blend-0.5)));\n";
+ code += "\t\t}\n";
+ code += "\t}\n";
+ }
+
+ } break;
+ case OP_HARD_LIGHT: {
+
+ for (int i = 0; i < 3; i++) {
+ code += "\t{\n";
+ code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += "\t\tif (base < 0.5) {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0*blend));\n";
+ code += "\t\t} else {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-2.0*(blend-0.5)));\n";
+ code += "\t\t}\n";
+ code += "\t}\n";
+ }
+
+ } break;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeColorOp::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeColorOp::Operator VisualShaderNodeColorOp::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeColorOp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeColorOp::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_SCREEN);
+ BIND_ENUM_CONSTANT(OP_DIFFERENCE);
+ BIND_ENUM_CONSTANT(OP_DARKEN);
+ BIND_ENUM_CONSTANT(OP_LIGHTEN);
+ BIND_ENUM_CONSTANT(OP_OVERLAY);
+ BIND_ENUM_CONSTANT(OP_DODGE);
+ BIND_ENUM_CONSTANT(OP_BURN);
+ BIND_ENUM_CONSTANT(OP_SOFT_LIGHT);
+ BIND_ENUM_CONSTANT(OP_HARD_LIGHT);
+}
+
+VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
+ op = OP_SCREEN;
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Transform Mult
+
+String VisualShaderNodeTransformMult::get_caption() const {
+ return "TransformMult";
+}
+
+int VisualShaderNodeTransformMult::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeTransformMult::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const {
+ return "mult"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ if (op == OP_AxB) {
+ return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
+ } else {
+ return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
+ }
+}
+
+void VisualShaderNodeTransformMult::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeTransformMult::Operator VisualShaderNodeTransformMult::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeTransformMult::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+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");
+
+ BIND_ENUM_CONSTANT(OP_AxB);
+ BIND_ENUM_CONSTANT(OP_BxA);
+}
+
+VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() {
+ op = OP_AxB;
+ set_input_port_default_value(0, Transform());
+ set_input_port_default_value(1, Transform());
+}
+
+////////////// TransformVec Mult
+
+String VisualShaderNodeTransformVecMult::get_caption() const {
+ return "TransformVectorMult";
+}
+
+int VisualShaderNodeTransformVecMult::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeTransformVecMult::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ if (op == OP_AxB) {
+ return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0) ).xyz;\n";
+ } else if (op == OP_BxA) {
+ return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + " ).xyz;\n";
+ } else if (op == OP_3x3_AxB) {
+ return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0) ).xyz;\n";
+ } else {
+ return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + " ).xyz;\n";
+ }
+}
+
+void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeTransformVecMult::Operator VisualShaderNodeTransformVecMult::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeTransformVecMult::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeTransformVecMult::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformVecMult::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformVecMult::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B (3x3),B x A (3x3)"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_AxB);
+ BIND_ENUM_CONSTANT(OP_BxA);
+ BIND_ENUM_CONSTANT(OP_3x3_AxB);
+ BIND_ENUM_CONSTANT(OP_3x3_BxA);
+}
+
+VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() {
+ op = OP_AxB;
+ set_input_port_default_value(0, Transform());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Scalar Func
+
+String VisualShaderNodeScalarFunc::get_caption() const {
+ return "ScalarFunc";
+}
+
+int VisualShaderNodeScalarFunc::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeScalarFunc::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+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) const {
+
+ static const char *scalar_func_id[FUNC_NEGATE + 1] = {
+ "sin($)",
+ "cos($)",
+ "tan($)",
+ "asin($)",
+ "acos($)",
+ "atan($)",
+ "sinh($)",
+ "cosh($)",
+ "tanh($)",
+ "log($)",
+ "exp($)",
+ "sqrt($)",
+ "abs($)",
+ "sign($)",
+ "floor($)",
+ "round($)",
+ "ceil($)",
+ "fract($)",
+ "min(max($,0),1)",
+ "-($)",
+ };
+
+ return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+}
+
+void VisualShaderNodeScalarFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeScalarFunc::Function VisualShaderNodeScalarFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeScalarFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+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");
+
+ BIND_ENUM_CONSTANT(FUNC_SIN);
+ BIND_ENUM_CONSTANT(FUNC_COS);
+ BIND_ENUM_CONSTANT(FUNC_TAN);
+ BIND_ENUM_CONSTANT(FUNC_ASIN);
+ BIND_ENUM_CONSTANT(FUNC_ACOS);
+ BIND_ENUM_CONSTANT(FUNC_ATAN);
+ BIND_ENUM_CONSTANT(FUNC_SINH);
+ BIND_ENUM_CONSTANT(FUNC_COSH);
+ BIND_ENUM_CONSTANT(FUNC_TANH);
+ BIND_ENUM_CONSTANT(FUNC_LOG);
+ BIND_ENUM_CONSTANT(FUNC_EXP);
+ BIND_ENUM_CONSTANT(FUNC_SQRT);
+ BIND_ENUM_CONSTANT(FUNC_ABS);
+ BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_FLOOR);
+ BIND_ENUM_CONSTANT(FUNC_ROUND);
+ BIND_ENUM_CONSTANT(FUNC_CEIL);
+ BIND_ENUM_CONSTANT(FUNC_FRAC);
+ BIND_ENUM_CONSTANT(FUNC_SATURATE);
+ BIND_ENUM_CONSTANT(FUNC_NEGATE);
+}
+
+VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() {
+ func = FUNC_SIGN;
+ set_input_port_default_value(0, 0.0);
+}
+
+////////////// Vector Func
+
+String VisualShaderNodeVectorFunc::get_caption() const {
+ return "VectorFunc";
+}
+
+int VisualShaderNodeVectorFunc::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeVectorFunc::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+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) const {
+
+ static const char *vec_func_id[FUNC_HSV2RGB + 1] = {
+ "normalize($)",
+ "max(min($,vec3(1.0)),vec3(0.0))",
+ "-($)",
+ "1.0/($)",
+ "",
+ "",
+ };
+
+ String code;
+
+ if (func == FUNC_RGB2HSV) {
+ code += "\t{\n";
+ code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
+ code += "\t\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
+ code += "\t\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
+ code += "\t\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
+ code += "\t\tfloat d = q.x - min(q.w, q.y);\n";
+ code += "\t\tfloat e = 1.0e-10;\n";
+ code += "\t\t" + p_output_vars[0] + "=vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
+ code += "\t}\n";
+ } else if (func == FUNC_HSV2RGB) {
+ code += "\t{\n";
+ code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
+ code += "\t\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
+ code += "\t\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
+ code += "\t\t" + p_output_vars[0] + "=c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
+ code += "\t}\n";
+
+ } else {
+ code += "\t" + p_output_vars[0] + "=" + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ }
+
+ return code;
+}
+
+void VisualShaderNodeVectorFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeVectorFunc::Function VisualShaderNodeVectorFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+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");
+
+ BIND_ENUM_CONSTANT(FUNC_NORMALIZE);
+ BIND_ENUM_CONSTANT(FUNC_SATURATE);
+ BIND_ENUM_CONSTANT(FUNC_NEGATE);
+ BIND_ENUM_CONSTANT(FUNC_RECIPROCAL);
+ BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
+ BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
+}
+
+VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
+ func = FUNC_NORMALIZE;
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Dot Product
+
+String VisualShaderNodeDotProduct::get_caption() const {
+ return "DotProduct";
+}
+
+int VisualShaderNodeDotProduct::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeDotProduct::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const {
+ return "dot";
+}
+
+String VisualShaderNodeDotProduct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = dot( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n";
+}
+
+VisualShaderNodeDotProduct::VisualShaderNodeDotProduct() {
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Vector Len
+
+String VisualShaderNodeVectorLen::get_caption() const {
+ return "VectorLen";
+}
+
+int VisualShaderNodeVectorLen::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeVectorLen::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const {
+ return "length";
+}
+
+String VisualShaderNodeVectorLen::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = length( " + p_input_vars[0] + " );\n";
+}
+
+VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() {
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Scalar Interp
+
+String VisualShaderNodeScalarInterp::get_caption() const {
+ return "ScalarInterp";
+}
+
+int VisualShaderNodeScalarInterp::get_input_port_count() const {
+ return 3;
+}
+VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarInterp::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "a";
+ } else if (p_port == 1) {
+ return "b";
+ } else {
+ return "c";
+ }
+}
+
+int VisualShaderNodeScalarInterp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarInterp::get_output_port_name(int p_port) const {
+ return "mix";
+}
+
+String VisualShaderNodeScalarInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeScalarInterp::VisualShaderNodeScalarInterp() {
+ 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 Interp
+
+String VisualShaderNodeVectorInterp::get_caption() const {
+ return "VectorInterp";
+}
+
+int VisualShaderNodeVectorInterp::get_input_port_count() const {
+ return 3;
+}
+VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorInterp::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "a";
+ } else if (p_port == 1) {
+ return "b";
+ } else {
+ return "c";
+ }
+}
+
+int VisualShaderNodeVectorInterp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorInterp::get_output_port_name(int p_port) const {
+ return "mix";
+}
+
+String VisualShaderNodeVectorInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorInterp::VisualShaderNodeVectorInterp() {
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+ set_input_port_default_value(2, Vector3());
+}
+
+////////////// Vector Compose
+String VisualShaderNodeVectorCompose::get_caption() const {
+ return "VectorCompose";
+}
+
+int VisualShaderNodeVectorCompose::get_input_port_count() const {
+ return 3;
+}
+VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else {
+ return "z";
+ }
+}
+
+int VisualShaderNodeVectorCompose::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const {
+ return "vec";
+}
+
+String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = vec3( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() {
+
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+}
+
+////////////// Transform Compose
+
+String VisualShaderNodeTransformCompose::get_caption() const {
+ return "TransformCompose";
+}
+
+int VisualShaderNodeTransformCompose::get_input_port_count() const {
+ return 4;
+}
+VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else if (p_port == 2) {
+ return "z";
+ } else {
+ return "origin";
+ }
+}
+
+int VisualShaderNodeTransformCompose::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformCompose::get_output_port_name(int p_port) const {
+ return "xform";
+}
+
+String VisualShaderNodeTransformCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = mat4( vec4(" + p_input_vars[0] + ", 0.0) , vec4(" + p_input_vars[1] + ", 0.0) , vec4(" + p_input_vars[2] + ",0.0), vec4(" + p_input_vars[3] + ",1.0) );\n";
+}
+
+VisualShaderNodeTransformCompose::VisualShaderNodeTransformCompose() {
+
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+ set_input_port_default_value(2, Vector3());
+ set_input_port_default_value(3, Vector3());
+}
+
+////////////// Vector Decompose
+String VisualShaderNodeVectorDecompose::get_caption() const {
+ return "VectorDecompose";
+}
+
+int VisualShaderNodeVectorDecompose::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const {
+ return "vec";
+}
+
+int VisualShaderNodeVectorDecompose::get_output_port_count() const {
+ return 3;
+}
+VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else {
+ return "z";
+ }
+}
+
+String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ String code;
+ code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ return code;
+}
+
+VisualShaderNodeVectorDecompose::VisualShaderNodeVectorDecompose() {
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Transform Decompose
+
+String VisualShaderNodeTransformDecompose::get_caption() const {
+ return "TransformDecompose";
+}
+
+int VisualShaderNodeTransformDecompose::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformDecompose::get_input_port_name(int p_port) const {
+ return "xform";
+}
+
+int VisualShaderNodeTransformDecompose::get_output_port_count() const {
+ return 4;
+}
+VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else if (p_port == 2) {
+ return "z";
+ } else {
+ return "origin";
+ }
+}
+
+String VisualShaderNodeTransformDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ String code;
+ code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n";
+ code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n";
+ code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n";
+ code += "\t" + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n";
+ return code;
+}
+
+VisualShaderNodeTransformDecompose::VisualShaderNodeTransformDecompose() {
+ set_input_port_default_value(0, Transform());
+}
+
+////////////// Scalar Uniform
+
+String VisualShaderNodeScalarUniform::get_caption() const {
+ return "ScalarUniform";
+}
+
+int VisualShaderNodeScalarUniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeScalarUniform::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarUniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform float " + get_uniform_name() + ";\n";
+}
+String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeScalarUniform::VisualShaderNodeScalarUniform() {
+}
+
+////////////// Color Uniform
+
+String VisualShaderNodeColorUniform::get_caption() const {
+ return "ColorUniform";
+}
+
+int VisualShaderNodeColorUniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeColorUniform::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+
+ return "uniform vec4 " + get_uniform_name() + " : hint_color;\n";
+}
+
+String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
+ code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
+ return code;
+}
+
+VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() {
+}
+
+////////////// Vector Uniform
+
+String VisualShaderNodeVec3Uniform::get_caption() const {
+ return "VectorUniform";
+}
+
+int VisualShaderNodeVec3Uniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec3Uniform::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform vec3 " + get_uniform_name() + ";\n";
+}
+
+String VisualShaderNodeVec3Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() {
+}
+
+////////////// Transform Uniform
+
+String VisualShaderNodeTransformUniform::get_caption() const {
+ return "TransformUniform";
+}
+
+int VisualShaderNodeTransformUniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeTransformUniform::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformUniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform mat4 " + get_uniform_name() + ";\n";
+}
+
+String VisualShaderNodeTransformUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() {
+}
+
+////////////// Texture Uniform
+
+String VisualShaderNodeTextureUniform::get_caption() const {
+ return "TextureUniform";
+}
+
+int VisualShaderNodeTextureUniform::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "uv" : "lod";
+}
+
+int VisualShaderNodeTextureUniform::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code = "uniform sampler2D " + get_uniform_name();
+
+ switch (texture_type) {
+ case TYPE_DATA:
+ if (color_default == COLOR_DEFAULT_BLACK)
+ code += " : hint_black;\n";
+ else
+ code += ";\n";
+ break;
+ case TYPE_COLOR:
+ if (color_default == COLOR_DEFAULT_BLACK)
+ code += " : hint_black_albedo;\n";
+ else
+ code += " : hint_albedo;\n";
+ break;
+ case TYPE_NORMALMAP: code += " : hint_normal;\n"; break;
+ case TYPE_ANISO: code += " : hint_aniso;\n"; break;
+ }
+
+ return code;
+}
+
+String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String id = get_uniform_name();
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 n_tex_read = vec4(0.0);\n";
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 n_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\t\tvec4 n_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+}
+
+void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) {
+
+ texture_type = p_type;
+ emit_changed();
+}
+
+VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_texture_type() const {
+ return texture_type;
+}
+
+void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) {
+ color_default = p_default;
+ emit_changed();
+}
+VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get_color_default() const {
+ return color_default;
+}
+
+Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("texture_type");
+ props.push_back("color_default");
+ return props;
+}
+
+void VisualShaderNodeTextureUniform::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type);
+ ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type);
+
+ ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default);
+ ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default");
+
+ BIND_ENUM_CONSTANT(TYPE_DATA);
+ BIND_ENUM_CONSTANT(TYPE_COLOR);
+ BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+ BIND_ENUM_CONSTANT(TYPE_ANISO);
+
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE);
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK);
+}
+
+VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() {
+ texture_type = TYPE_DATA;
+ color_default = COLOR_DEFAULT_WHITE;
+}
+
+////////////// CubeMap Uniform
+
+String VisualShaderNodeCubeMapUniform::get_caption() const {
+ return "CubeMapUniform";
+}
+
+int VisualShaderNodeCubeMapUniform::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "normal" : "lod";
+}
+
+int VisualShaderNodeCubeMapUniform::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return String();
+}
+
+VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() {
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
new file mode 100644
index 0000000000..2ede36fbc8
--- /dev/null
+++ b/scene/resources/visual_shader_nodes.h
@@ -0,0 +1,861 @@
+#ifndef VISUAL_SHADER_NODES_H
+#define VISUAL_SHADER_NODES_H
+
+#include "scene/resources/visual_shader.h"
+
+/// CONSTANTS ///
+
+class VisualShaderNodeScalarConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarConstant, VisualShaderNode)
+ float 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) 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(float p_value);
+ float get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarConstant();
+};
+
+class VisualShaderNodeColorConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode)
+ Color 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) 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(Color p_value);
+ Color get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeColorConstant();
+};
+
+class VisualShaderNodeVec3Constant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode)
+ Vector3 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) 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(Vector3 p_value);
+ Vector3 get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVec3Constant();
+};
+
+class VisualShaderNodeTransformConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode)
+ Transform 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) 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(Transform p_value);
+ Transform get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformConstant();
+};
+
+//////////////////////////////////
+
+class VisualShaderNodeTexture : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTexture, VisualShaderNode)
+ Ref<Texture> texture;
+
+public:
+ enum Source {
+ SOURCE_TEXTURE,
+ SOURCE_SCREEN,
+ SOURCE_2D_TEXTURE,
+ SOURCE_2D_NORMAL
+ };
+
+ enum TextureType {
+ TYPE_DATA,
+ TYPE_COLOR,
+ TYPE_NORMALMAP
+ };
+
+private:
+ Source source;
+ TextureType texture_type;
+
+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 Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) 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) 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_source(Source p_source);
+ Source get_source() const;
+
+ void set_texture(Ref<Texture> p_value);
+ Ref<Texture> get_texture() const;
+
+ void set_texture_type(TextureType p_type);
+ TextureType get_texture_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const;
+
+ VisualShaderNodeTexture();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTexture::TextureType)
+VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source)
+
+//////////////////////////////////
+
+class VisualShaderNodeCubeMap : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeCubeMap, VisualShaderNode)
+ Ref<CubeMap> cube_map;
+
+public:
+ enum TextureType {
+ TYPE_DATA,
+ TYPE_COLOR,
+ TYPE_NORMALMAP
+ };
+
+private:
+ TextureType texture_type;
+
+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 Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) 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) 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_cube_map(Ref<CubeMap> p_value);
+ Ref<CubeMap> get_cube_map() const;
+
+ void set_texture_type(TextureType p_type);
+ TextureType get_texture_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeCubeMap();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType)
+///////////////////////////////////////
+
+class VisualShaderNodeScalarOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarOp, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_POW,
+ OP_MAX,
+ OP_MIN,
+ OP_ATAN2
+ };
+
+protected:
+ Operator op;
+
+ 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) 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_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarOp();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeScalarOp::Operator)
+
+class VisualShaderNodeVectorOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorOp, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_POW,
+ OP_MAX,
+ OP_MIN,
+ OP_CROSS
+
+ };
+
+protected:
+ Operator op;
+
+ 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) 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_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVectorOp();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorOp::Operator)
+
+class VisualShaderNodeColorOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeColorOp, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_SCREEN,
+ OP_DIFFERENCE,
+ OP_DARKEN,
+ OP_LIGHTEN,
+ OP_OVERLAY,
+ OP_DODGE,
+ OP_BURN,
+ OP_SOFT_LIGHT,
+ OP_HARD_LIGHT,
+ };
+
+protected:
+ Operator op;
+
+ 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) 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_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeColorOp();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator)
+
+class VisualShaderNodeTransformMult : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_AxB,
+ OP_BxA,
+ };
+
+protected:
+ Operator op;
+
+ 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) 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_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformMult();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator)
+
+class VisualShaderNodeTransformVecMult : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformVecMult, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_AxB,
+ OP_BxA,
+ OP_3x3_AxB,
+ OP_3x3_BxA,
+ };
+
+protected:
+ Operator op;
+
+ 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) 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_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformVecMult();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTransformVecMult::Operator)
+
+///////////////////////////////////////
+
+class VisualShaderNodeScalarFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_SIN,
+ FUNC_COS,
+ FUNC_TAN,
+ FUNC_ASIN,
+ FUNC_ACOS,
+ FUNC_ATAN,
+ FUNC_SINH,
+ FUNC_COSH,
+ FUNC_TANH,
+ FUNC_LOG,
+ FUNC_EXP,
+ FUNC_SQRT,
+ FUNC_ABS,
+ FUNC_SIGN,
+ FUNC_FLOOR,
+ FUNC_ROUND,
+ FUNC_CEIL,
+ FUNC_FRAC,
+ FUNC_SATURATE,
+ FUNC_NEGATE,
+ };
+
+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) 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_func);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeScalarFunc::Function)
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_NORMALIZE,
+ FUNC_SATURATE,
+ FUNC_NEGATE,
+ FUNC_RECIPROCAL,
+ FUNC_RGB2HSV,
+ FUNC_HSV2RGB,
+ };
+
+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) 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_func);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVectorFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorFunc::Function)
+
+///////////////////////////////////////
+
+class VisualShaderNodeDotProduct : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDotProduct, 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) 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
+
+ VisualShaderNodeDotProduct();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorLen : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorLen, 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) 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
+
+ VisualShaderNodeVectorLen();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeScalarInterp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarInterp, 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) 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
+
+ VisualShaderNodeScalarInterp();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorInterp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorInterp, 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) 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
+
+ VisualShaderNodeVectorInterp();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorCompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorCompose, 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) 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
+
+ VisualShaderNodeVectorCompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeTransformCompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformCompose, 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) 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
+
+ VisualShaderNodeTransformCompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorDecompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorDecompose, 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) 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
+
+ VisualShaderNodeVectorDecompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeTransformDecompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformDecompose, 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) 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
+
+ VisualShaderNodeTransformDecompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeScalarUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeScalarUniform, 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) 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
+
+ VisualShaderNodeScalarUniform();
+};
+
+class VisualShaderNodeColorUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeColorUniform, 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) 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
+
+ VisualShaderNodeColorUniform();
+};
+
+class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeVec3Uniform, 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) 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
+
+ VisualShaderNodeVec3Uniform();
+};
+
+class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeTransformUniform, 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) 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
+
+ VisualShaderNodeTransformUniform();
+};
+
+//////////////////////////////////
+
+class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform)
+public:
+ enum TextureType {
+ TYPE_DATA,
+ TYPE_COLOR,
+ TYPE_NORMALMAP,
+ TYPE_ANISO,
+ };
+
+ enum ColorDefault {
+ COLOR_DEFAULT_WHITE,
+ COLOR_DEFAULT_BLACK
+ };
+
+private:
+ TextureType texture_type;
+ ColorDefault color_default;
+
+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_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) 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
+
+ Vector<StringName> get_editable_properties() const;
+
+ void set_texture_type(TextureType p_type);
+ TextureType get_texture_type() const;
+
+ void set_color_default(ColorDefault p_default);
+ ColorDefault get_color_default() const;
+
+ VisualShaderNodeTextureUniform();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType)
+VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault)
+
+//////////////////////////////////
+
+class VisualShaderNodeCubeMapUniform : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeCubeMapUniform, 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) 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
+
+ VisualShaderNodeCubeMapUniform();
+};
+
+#endif // VISUAL_SHADER_NODES_H