diff options
50 files changed, 1873 insertions, 1610 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bd4eb906c0..7ac048b24a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,6 +15,7 @@ SCsub @godotengine/buildsystem /core/ @godotengine/core /core/crypto/ @godotengine/network /core/debugger/ @godotengine/debugger +/core/extension/ @godotengine/gdextension /core/input/ @godotengine/input # Doc @@ -110,10 +111,9 @@ doc_classes/* @godotengine/documentation /modules/xatlas_unwrap/ @godotengine/rendering ## Scripting -/modules/gdnative/ @godotengine/gdnative /modules/gdscript/ @godotengine/gdscript /modules/jsonrpc/ @godotengine/gdscript -/modules/mono/ @godotengine/mono +/modules/mono/ @godotengine/dotnet ## Text /modules/freetype/ @godotengine/buildsystem @@ -73,5 +73,4 @@ for more information. [![Code Triagers Badge](https://www.codetriage.com/godotengine/godot/badges/users.svg)](https://www.codetriage.com/godotengine/godot) [![Translate on Weblate](https://hosted.weblate.org/widgets/godot-engine/-/godot/svg-badge.svg)](https://hosted.weblate.org/engage/godot-engine/?utm_source=widget) -[![Total alerts on LGTM](https://img.shields.io/lgtm/alerts/g/godotengine/godot.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/godotengine/godot/alerts) [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/godotengine/godot)](https://www.tickgit.com/browse?repo=github.com/godotengine/godot) diff --git a/SConstruct b/SConstruct index c86422c4e4..646f41839d 100644 --- a/SConstruct +++ b/SConstruct @@ -518,7 +518,7 @@ if selected_platform in platform_list: # are actually handled to change compile options, etc. detect.configure(env) - print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}", target "{env["target"]}.') + print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}", target "{env["target"]}".') if env.dev_build: print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).") diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index ad67cfa852..c30acf32bb 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -30,6 +30,8 @@ #include "a_star_grid_2d.h" +#include "core/variant/typed_array.h" + static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) { real_t dx = (real_t)ABS(p_to.x - p_from.x); real_t dy = (real_t)ABS(p_to.y - p_from.y); @@ -492,17 +494,17 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec return path; } -Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { - ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height)); +TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { + ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height)); Point *a = _get_point(p_from_id.x, p_from_id.y); Point *b = _get_point(p_to_id.x, p_to_id.y); if (a == b) { - Vector<Vector2> ret; - ret.push_back(Vector2((float)a->id.x, (float)a->id.y)); + TypedArray<Vector2i> ret; + ret.push_back(a); return ret; } @@ -511,7 +513,7 @@ Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector bool found_route = _solve(begin_point, end_point); if (!found_route) { - return Vector<Vector2>(); + return TypedArray<Vector2i>(); } Point *p = end_point; @@ -521,20 +523,18 @@ Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector p = p->prev_point; } - Vector<Vector2> path; + TypedArray<Vector2i> path; path.resize(pc); { - Vector2 *w = path.ptrw(); - p = end_point; int64_t idx = pc - 1; while (p != begin_point) { - w[idx--] = Vector2((float)p->id.x, (float)p->id.y); + path[idx--] = p->id; p = p->prev_point; } - w[0] = p->id; + path[0] = p->id; } return path; diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index bf6363aa01..1002f18738 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -169,7 +169,7 @@ public: void clear(); Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to); - Vector<Vector2> get_id_path(const Vector2i &p_from, const Vector2i &p_to); + TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to); }; VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode); diff --git a/core/math/bvh_abb.h b/core/math/bvh_abb.h index 8a44f1c4da..699f7de604 100644 --- a/core/math/bvh_abb.h +++ b/core/math/bvh_abb.h @@ -251,7 +251,9 @@ struct BVH_ABB { void expand(real_t p_change) { POINT change; - change.set_all(p_change); + for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) { + change[axis] = p_change; + } grow(change); } @@ -262,7 +264,9 @@ struct BVH_ABB { } void set_to_max_opposite_extents() { - neg_max.set_all(FLT_MAX); + for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) { + neg_max[axis] = FLT_MAX; + } min = neg_max; } diff --git a/core/math/vector2.h b/core/math/vector2.h index 9441f84087..75364f72f0 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -69,10 +69,6 @@ struct _NO_DISCARD_ Vector2 { return coord[p_idx]; } - _FORCE_INLINE_ void set_all(const real_t p_value) { - x = y = p_value; - } - _FORCE_INLINE_ Vector2::Axis min_axis_index() const { return x < y ? Vector2::AXIS_X : Vector2::AXIS_Y; } diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 4db45fe798..55ba509144 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -45,16 +45,6 @@ Vector3 Vector3::rotated(const Vector3 &p_axis, const real_t p_angle) const { return r; } -void Vector3::set_axis(const int p_axis, const real_t p_value) { - ERR_FAIL_INDEX(p_axis, 3); - coord[p_axis] = p_value; -} - -real_t Vector3::get_axis(const int p_axis) const { - ERR_FAIL_INDEX_V(p_axis, 3, 0); - return operator[](p_axis); -} - Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const { return Vector3( CLAMP(x, p_min.x, p_max.x), diff --git a/core/math/vector3.h b/core/math/vector3.h index 3944afa92e..62e810fb4d 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -68,13 +68,6 @@ struct _NO_DISCARD_ Vector3 { return coord[p_axis]; } - void set_axis(const int p_axis, const real_t p_value); - real_t get_axis(const int p_axis) const; - - _FORCE_INLINE_ void set_all(const real_t p_value) { - x = y = z = p_value; - } - _FORCE_INLINE_ Vector3::Axis min_axis_index() const { return x < y ? (x < z ? Vector3::AXIS_X : Vector3::AXIS_Z) : (y < z ? Vector3::AXIS_Y : Vector3::AXIS_Z); } diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp index b8e74ea6d2..b248f35035 100644 --- a/core/math/vector3i.cpp +++ b/core/math/vector3i.cpp @@ -33,16 +33,6 @@ #include "core/math/vector3.h" #include "core/string/ustring.h" -void Vector3i::set_axis(const int p_axis, const int32_t p_value) { - ERR_FAIL_INDEX(p_axis, 3); - coord[p_axis] = p_value; -} - -int32_t Vector3i::get_axis(const int p_axis) const { - ERR_FAIL_INDEX_V(p_axis, 3, 0); - return operator[](p_axis); -} - Vector3i::Axis Vector3i::min_axis_index() const { return x < y ? (x < z ? Vector3i::AXIS_X : Vector3i::AXIS_Z) : (y < z ? Vector3i::AXIS_Y : Vector3i::AXIS_Z); } diff --git a/core/math/vector3i.h b/core/math/vector3i.h index c6d03cd031..710fd96376 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -66,9 +66,6 @@ struct _NO_DISCARD_ Vector3i { return coord[p_axis]; } - void set_axis(const int p_axis, const int32_t p_value); - int32_t get_axis(const int p_axis) const; - Vector3i::Axis min_axis_index() const; Vector3i::Axis max_axis_index() const; diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp index 3c25f454a3..55e51834df 100644 --- a/core/math/vector4.cpp +++ b/core/math/vector4.cpp @@ -33,16 +33,6 @@ #include "core/math/basis.h" #include "core/string/print_string.h" -void Vector4::set_axis(const int p_axis, const real_t p_value) { - ERR_FAIL_INDEX(p_axis, 4); - components[p_axis] = p_value; -} - -real_t Vector4::get_axis(const int p_axis) const { - ERR_FAIL_INDEX_V(p_axis, 4, 0); - return operator[](p_axis); -} - Vector4::Axis Vector4::min_axis_index() const { uint32_t min_index = 0; real_t min_value = x; diff --git a/core/math/vector4.h b/core/math/vector4.h index d89f3ddb05..426c473e13 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -65,11 +65,6 @@ struct _NO_DISCARD_ Vector4 { return components[p_axis]; } - _FORCE_INLINE_ void set_all(const real_t p_value); - - void set_axis(const int p_axis, const real_t p_value); - real_t get_axis(const int p_axis) const; - Vector4::Axis min_axis_index() const; Vector4::Axis max_axis_index() const; @@ -150,10 +145,6 @@ struct _NO_DISCARD_ Vector4 { } }; -void Vector4::set_all(const real_t p_value) { - x = y = z = p_value; -} - real_t Vector4::dot(const Vector4 &p_vec4) const { return x * p_vec4.x + y * p_vec4.y + z * p_vec4.z + w * p_vec4.w; } diff --git a/core/math/vector4i.cpp b/core/math/vector4i.cpp index a89b802675..77f6fbd5b7 100644 --- a/core/math/vector4i.cpp +++ b/core/math/vector4i.cpp @@ -33,16 +33,6 @@ #include "core/math/vector4.h" #include "core/string/ustring.h" -void Vector4i::set_axis(const int p_axis, const int32_t p_value) { - ERR_FAIL_INDEX(p_axis, 4); - coord[p_axis] = p_value; -} - -int32_t Vector4i::get_axis(const int p_axis) const { - ERR_FAIL_INDEX_V(p_axis, 4, 0); - return operator[](p_axis); -} - Vector4i::Axis Vector4i::min_axis_index() const { uint32_t min_index = 0; int32_t min_value = x; diff --git a/core/math/vector4i.h b/core/math/vector4i.h index fdf33b9569..a32414bb18 100644 --- a/core/math/vector4i.h +++ b/core/math/vector4i.h @@ -68,9 +68,6 @@ struct _NO_DISCARD_ Vector4i { return coord[p_axis]; } - void set_axis(const int p_axis, const int32_t p_value); - int32_t get_axis(const int p_axis) const; - Vector4i::Axis min_axis_index() const; Vector4i::Axis max_axis_index() const; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 75b797d397..6218c21cde 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -4651,15 +4651,18 @@ String String::sprintf(const Array &values, bool *error) const { double value = values[value_index]; bool is_negative = (value < 0); String str = String::num(ABS(value), min_decimals); + bool not_numeric = isinf(value) || isnan(value); // Pad decimals out. - str = str.pad_decimals(min_decimals); + if (!not_numeric) { + str = str.pad_decimals(min_decimals); + } int initial_len = str.length(); // Padding. Leave room for sign later if required. int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeros ? String("0") : String(" "); + String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros if (left_justified) { str = str.rpad(pad_chars_count, pad_char); } else { @@ -4709,14 +4712,19 @@ String String::sprintf(const Array &values, bool *error) const { String str = "("; for (int i = 0; i < count; i++) { double val = vec[i]; + String number_str = String::num(ABS(val), min_decimals); + bool not_numeric = isinf(val) || isnan(val); + // Pad decimals out. - String number_str = String::num(ABS(val), min_decimals).pad_decimals(min_decimals); + if (!not_numeric) { + number_str = number_str.pad_decimals(min_decimals); + } int initial_len = number_str.length(); // Padding. Leave room for sign later if required. int pad_chars_count = val < 0 ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeros ? String("0") : String(" "); + String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros if (left_justified) { number_str = number_str.rpad(pad_chars_count, pad_char); } else { diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 331862ebfa..8dde3748d7 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -53,7 +53,7 @@ </description> </method> <method name="get_id_path"> - <return type="PackedVector2Array" /> + <return type="Vector2i[]" /> <param index="0" name="from_id" type="Vector2i" /> <param index="1" name="to_id" type="Vector2i" /> <description> diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index d62f704528..6d9b679fbc 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -83,7 +83,7 @@ <return type="Vector3" /> <param index="0" name="order" type="int" default="2" /> <description> - Returns the basis's rotation in the form of Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last). The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle). + Returns the basis's rotation in the form of Euler angles. The Euler order depends on the [param order] parameter, by default it uses the YXZ convention: when decomposing, first Z, then X, and Y last. The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle). Consider using the [method get_rotation_quaternion] method instead, which returns a [Quaternion] quaternion instead of Euler angles. </description> </method> diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 270f4560b7..65c65a517f 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -716,11 +716,11 @@ void EditorProperty::shortcut_input(const Ref<InputEvent> &p_event) { const Ref<InputEventKey> k = p_event; if (k.is_valid() && k->is_pressed()) { - if (ED_IS_SHORTCUT("property_editor/copy_property", p_event)) { - menu_option(MENU_COPY_PROPERTY); + if (ED_IS_SHORTCUT("property_editor/copy_value", p_event)) { + menu_option(MENU_COPY_VALUE); accept_event(); - } else if (ED_IS_SHORTCUT("property_editor/paste_property", p_event) && !is_read_only()) { - menu_option(MENU_PASTE_PROPERTY); + } else if (ED_IS_SHORTCUT("property_editor/paste_value", p_event) && !is_read_only()) { + menu_option(MENU_PASTE_VALUE); accept_event(); } else if (ED_IS_SHORTCUT("property_editor/copy_property_path", p_event)) { menu_option(MENU_COPY_PROPERTY_PATH); @@ -915,10 +915,10 @@ Control *EditorProperty::make_custom_tooltip(const String &p_text) const { void EditorProperty::menu_option(int p_option) { switch (p_option) { - case MENU_COPY_PROPERTY: { + case MENU_COPY_VALUE: { InspectorDock::get_inspector_singleton()->set_property_clipboard(object->get(property)); } break; - case MENU_PASTE_PROPERTY: { + case MENU_PASTE_VALUE: { emit_changed(property, InspectorDock::get_inspector_singleton()->get_property_clipboard()); } break; case MENU_COPY_PROPERTY_PATH: { @@ -1013,10 +1013,10 @@ void EditorProperty::_update_popup() { add_child(menu); menu->connect("id_pressed", callable_mp(this, &EditorProperty::menu_option)); } - menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/copy_property"), MENU_COPY_PROPERTY); - menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/paste_property"), MENU_PASTE_PROPERTY); + menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/copy_value"), MENU_COPY_VALUE); + menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/paste_value"), MENU_PASTE_VALUE); menu->add_icon_shortcut(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/copy_property_path"), MENU_COPY_PROPERTY_PATH); - menu->set_item_disabled(MENU_PASTE_PROPERTY, is_read_only()); + menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only()); if (!pin_hidden) { menu->add_separator(); if (can_pin) { @@ -4101,7 +4101,7 @@ EditorInspector::EditorInspector() { refresh_countdown = 0.33; } - ED_SHORTCUT("property_editor/copy_property", TTR("Copy Property"), KeyModifierMask::CMD_OR_CTRL | Key::C); - ED_SHORTCUT("property_editor/paste_property", TTR("Paste Property"), KeyModifierMask::CMD_OR_CTRL | Key::V); + ED_SHORTCUT("property_editor/copy_value", TTR("Copy Value"), KeyModifierMask::CMD_OR_CTRL | Key::C); + ED_SHORTCUT("property_editor/paste_value", TTR("Paste Value"), KeyModifierMask::CMD_OR_CTRL | Key::V); ED_SHORTCUT("property_editor/copy_property_path", TTR("Copy Property Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 872007e637..bada02e254 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -58,8 +58,8 @@ class EditorProperty : public Container { public: enum MenuItems { - MENU_COPY_PROPERTY, - MENU_PASTE_PROPERTY, + MENU_COPY_VALUE, + MENU_PASTE_VALUE, MENU_COPY_PROPERTY_PATH, MENU_PIN_VALUE, MENU_OPEN_DOCUMENTATION, diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index de8259c25c..f89aef4cc3 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -155,7 +155,7 @@ void EditorResourcePicker::_file_selected(const String &p_path) { any_type_matches = is_global_class ? EditorNode::get_editor_data().script_class_is_parent(res_type, base) : loaded_resource->is_class(base); - if (!any_type_matches) { + if (any_type_matches) { break; } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 18561fe3a0..4dcd0573e6 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -49,6 +49,7 @@ #include "editor/find_in_files.h" #include "editor/node_dock.h" #include "editor/plugins/shader_editor_plugin.h" +#include "editor/plugins/text_shader_editor.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" #include "script_text_editor.h" @@ -1124,6 +1125,7 @@ TypedArray<Script> ScriptEditor::_get_open_scripts() const { bool ScriptEditor::toggle_scripts_panel() { list_split->set_visible(!list_split->is_visible()); + EditorSettings::get_singleton()->set_project_metadata("scripts_panel", "show_scripts_panel", list_split->is_visible()); return list_split->is_visible(); } @@ -3697,6 +3699,7 @@ ScriptEditor::ScriptEditor() { overview_vbox->set_v_size_flags(SIZE_EXPAND_FILL); list_split->add_child(overview_vbox); + list_split->set_visible(EditorSettings::get_singleton()->get_project_metadata("scripts_panel", "show_scripts_panel", true)); buttons_hbox = memnew(HBoxContainer); overview_vbox->add_child(buttons_hbox); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 2eafa4fc91..de1a807721 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -30,1172 +30,12 @@ #include "shader_editor_plugin.h" -#include "core/io/resource_loader.h" -#include "core/io/resource_saver.h" -#include "core/os/keyboard.h" -#include "core/os/os.h" -#include "core/version_generated.gen.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" -#include "editor/editor_settings.h" #include "editor/filesystem_dock.h" +#include "editor/plugins/text_shader_editor.h" #include "editor/plugins/visual_shader_editor_plugin.h" -#include "editor/project_settings_editor.h" #include "editor/shader_create_dialog.h" -#include "scene/gui/split_container.h" -#include "servers/display_server.h" -#include "servers/rendering/shader_preprocessor.h" -#include "servers/rendering/shader_types.h" - -/*** SHADER SYNTAX HIGHLIGHTER ****/ - -Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) { - Dictionary color_map; - - for (const Point2i ®ion : disabled_branch_regions) { - if (p_line >= region.x && p_line <= region.y) { - Dictionary highlighter_info; - highlighter_info["color"] = disabled_branch_color; - - color_map[0] = highlighter_info; - return color_map; - } - } - - return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line); -} - -void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) { - ERR_FAIL_COND(p_region.x < 0); - ERR_FAIL_COND(p_region.y < 0); - - for (int i = 0; i < disabled_branch_regions.size(); i++) { - ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists."); - } - - Point2i disabled_branch_region; - disabled_branch_region.x = p_region.x; - disabled_branch_region.y = p_region.y; - disabled_branch_regions.push_back(disabled_branch_region); - - clear_highlighting_cache(); -} - -void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() { - disabled_branch_regions.clear(); - clear_highlighting_cache(); -} - -void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) { - disabled_branch_color = p_color; - clear_highlighting_cache(); -} - -/*** SHADER SCRIPT EDITOR ****/ - -static bool saved_warnings_enabled = false; -static bool saved_treat_warning_as_errors = false; -static HashMap<ShaderWarning::Code, bool> saved_warnings; -static uint32_t saved_warning_flags = 0U; - -void ShaderTextEditor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_THEME_CHANGED: { - if (is_visible_in_tree()) { - _load_theme_settings(); - if (warnings.size() > 0 && last_compile_result == OK) { - warnings_panel->clear(); - _update_warning_panel(); - } - } - } break; - } -} - -Ref<Shader> ShaderTextEditor::get_edited_shader() const { - return shader; -} - -Ref<ShaderInclude> ShaderTextEditor::get_edited_shader_include() const { - return shader_inc; -} - -void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) { - set_edited_shader(p_shader, p_shader->get_code()); -} - -void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const String &p_code) { - if (shader == p_shader) { - return; - } - if (shader.is_valid()) { - shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); - } - shader = p_shader; - shader_inc = Ref<ShaderInclude>(); - - set_edited_code(p_code); - - if (shader.is_valid()) { - shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); - } -} - -void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) { - set_edited_shader_include(p_shader_inc, p_shader_inc->get_code()); -} - -void ShaderTextEditor::_shader_changed() { - // This function is used for dependencies (include changing changes main shader and forces it to revalidate) - if (block_shader_changed) { - return; - } - dependencies_version++; - _validate_script(); -} - -void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) { - if (shader_inc == p_shader_inc) { - return; - } - if (shader_inc.is_valid()) { - shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); - } - shader_inc = p_shader_inc; - shader = Ref<Shader>(); - - set_edited_code(p_code); - - if (shader_inc.is_valid()) { - shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); - } -} - -void ShaderTextEditor::set_edited_code(const String &p_code) { - _load_theme_settings(); - - get_text_editor()->set_text(p_code); - get_text_editor()->clear_undo_history(); - get_text_editor()->call_deferred(SNAME("set_h_scroll"), 0); - get_text_editor()->call_deferred(SNAME("set_v_scroll"), 0); - get_text_editor()->tag_saved_version(); - - _validate_script(); - _line_col_changed(); -} - -void ShaderTextEditor::reload_text() { - ERR_FAIL_COND(shader.is_null()); - - CodeEdit *te = get_text_editor(); - int column = te->get_caret_column(); - int row = te->get_caret_line(); - int h = te->get_h_scroll(); - int v = te->get_v_scroll(); - - te->set_text(shader->get_code()); - te->set_caret_line(row); - te->set_caret_column(column); - te->set_h_scroll(h); - te->set_v_scroll(v); - - te->tag_saved_version(); - - update_line_and_column(); -} - -void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) { - warnings_panel = p_warnings_panel; -} - -void ShaderTextEditor::_load_theme_settings() { - CodeEdit *text_editor = get_text_editor(); - Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color"); - if (updated_marked_line_color != marked_line_color) { - for (int i = 0; i < text_editor->get_line_count(); i++) { - if (text_editor->get_line_background_color(i) == marked_line_color) { - text_editor->set_line_background_color(i, updated_marked_line_color); - } - } - marked_line_color = updated_marked_line_color; - } - - syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color")); - syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color")); - syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color")); - syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color")); - - syntax_highlighter->clear_keyword_colors(); - - const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color"); - const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color"); - - List<String> keywords; - ShaderLanguage::get_keyword_list(&keywords); - - for (const String &E : keywords) { - if (ShaderLanguage::is_control_flow_keyword(E)) { - syntax_highlighter->add_keyword_color(E, control_flow_keyword_color); - } else { - syntax_highlighter->add_keyword_color(E, keyword_color); - } - } - - List<String> pp_keywords; - ShaderPreprocessor::get_keyword_list(&pp_keywords, false); - - for (const String &E : pp_keywords) { - syntax_highlighter->add_keyword_color(E, keyword_color); - } - - // Colorize built-ins like `COLOR` differently to make them easier - // to distinguish from keywords at a quick glance. - - List<String> built_ins; - - if (shader_inc.is_valid()) { - for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { - for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) { - for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) { - built_ins.push_back(F.key); - } - } - - const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); - - for (int j = 0; j < modes.size(); j++) { - const ShaderLanguage::ModeInfo &info = modes[j]; - - if (!info.options.is_empty()) { - for (int k = 0; k < info.options.size(); k++) { - built_ins.push_back(String(info.name) + "_" + String(info.options[k])); - } - } else { - built_ins.push_back(String(info.name)); - } - } - } - } else if (shader.is_valid()) { - for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) { - for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) { - built_ins.push_back(F.key); - } - } - - const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); - - for (int i = 0; i < modes.size(); i++) { - const ShaderLanguage::ModeInfo &info = modes[i]; - - if (!info.options.is_empty()) { - for (int j = 0; j < info.options.size(); j++) { - built_ins.push_back(String(info.name) + "_" + String(info.options[j])); - } - } else { - built_ins.push_back(String(info.name)); - } - } - } - - const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color"); - - for (const String &E : built_ins) { - syntax_highlighter->add_keyword_color(E, user_type_color); - } - - // Colorize comments. - const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); - syntax_highlighter->clear_color_regions(); - syntax_highlighter->add_color_region("/*", "*/", comment_color, false); - syntax_highlighter->add_color_region("//", "", comment_color, true); - syntax_highlighter->set_disabled_branch_color(comment_color); - - text_editor->clear_comment_delimiters(); - text_editor->add_comment_delimiter("/*", "*/", false); - text_editor->add_comment_delimiter("//", "", true); - - if (!text_editor->has_auto_brace_completion_open_key("/*")) { - text_editor->add_auto_brace_completion_pair("/*", "*/"); - } - - // Colorize preprocessor include strings. - const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); - syntax_highlighter->add_color_region("\"", "\"", string_color, false); - - if (warnings_panel) { - // Warnings panel. - warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("main"), SNAME("EditorFonts"))); - warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts"))); - } -} - -void ShaderTextEditor::_check_shader_mode() { - String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text()); - - Shader::Mode mode; - - if (type == "canvas_item") { - mode = Shader::MODE_CANVAS_ITEM; - } else if (type == "particles") { - mode = Shader::MODE_PARTICLES; - } else if (type == "sky") { - mode = Shader::MODE_SKY; - } else if (type == "fog") { - mode = Shader::MODE_FOG; - } else { - mode = Shader::MODE_SPATIAL; - } - - if (shader->get_mode() != mode) { - set_block_shader_changed(true); - shader->set_code(get_text_editor()->get_text()); - set_block_shader_changed(false); - _load_theme_settings(); - } -} - -static ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_variable) { - RS::GlobalShaderParameterType gvt = RS::get_singleton()->global_shader_parameter_get_type(p_variable); - return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt); -} - -static String complete_from_path; - -static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) { - if (!p_efsd) { - return; - } - for (int i = 0; i < p_efsd->get_file_count(); i++) { - if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) { - String path = p_efsd->get_file_path(i); - if (path.begins_with(complete_from_path)) { - path = path.replace_first(complete_from_path, ""); - } - r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH)); - } - } - for (int j = 0; j < p_efsd->get_subdir_count(); j++) { - _complete_include_paths_search(p_efsd->get_subdir(j), r_options); - } -} - -static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) { - _complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options); -} - -void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) { - List<ScriptLanguage::CodeCompletionOption> pp_options; - List<ScriptLanguage::CodeCompletionOption> pp_defines; - ShaderPreprocessor preprocessor; - String code; - complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir(); - if (!complete_from_path.ends_with("/")) { - complete_from_path += "/"; - } - preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths); - complete_from_path = String(); - if (pp_options.size()) { - for (const ScriptLanguage::CodeCompletionOption &E : pp_options) { - r_options->push_back(E); - } - return; - } - for (const ScriptLanguage::CodeCompletionOption &E : pp_defines) { - r_options->push_back(E); - } - - ShaderLanguage sl; - String calltip; - ShaderLanguage::ShaderCompileInfo info; - info.global_shader_uniform_type_func = _get_global_shader_uniform_type; - - if (shader.is_null()) { - info.is_include = true; - - sl.complete(code, info, r_options, calltip); - get_text_editor()->set_code_hint(calltip); - return; - } - _check_shader_mode(); - info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())); - info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); - info.shader_types = ShaderTypes::get_singleton()->get_types(); - - sl.complete(code, info, r_options, calltip); - get_text_editor()->set_code_hint(calltip); -} - -void ShaderTextEditor::_validate_script() { - emit_signal(SNAME("script_changed")); // Ensure to notify that it changed, so it is applied - - String code; - - if (shader.is_valid()) { - _check_shader_mode(); - code = shader->get_code(); - } else { - code = shader_inc->get_code(); - } - - ShaderPreprocessor preprocessor; - String code_pp; - String error_pp; - List<ShaderPreprocessor::FilePosition> err_positions; - List<ShaderPreprocessor::Region> regions; - String filename; - if (shader.is_valid()) { - filename = shader->get_path(); - } else if (shader_inc.is_valid()) { - filename = shader_inc->get_path(); - } - last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, ®ions); - - for (int i = 0; i < get_text_editor()->get_line_count(); i++) { - get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); - } - - syntax_highlighter->clear_disabled_branch_regions(); - for (const ShaderPreprocessor::Region ®ion : regions) { - if (!region.enabled) { - if (filename != region.file) { - continue; - } - syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line)); - } - } - - set_error(""); - set_error_count(0); - - if (last_compile_result != OK) { - //preprocessor error - ERR_FAIL_COND(err_positions.size() == 0); - - String error_text = error_pp; - int error_line = err_positions.front()->get().line; - if (err_positions.size() == 1) { - // Error in main file - error_text = "error(" + itos(error_line) + "): " + error_text; - } else { - error_text = "error(" + itos(error_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + error_text; - set_error_count(err_positions.size() - 1); - } - - set_error(error_text); - set_error_pos(error_line - 1, 0); - for (int i = 0; i < get_text_editor()->get_line_count(); i++) { - get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); - } - get_text_editor()->set_line_background_color(error_line - 1, marked_line_color); - - set_warning_count(0); - - } else { - ShaderLanguage sl; - - sl.enable_warning_checking(saved_warnings_enabled); - uint32_t flags = saved_warning_flags; - if (shader.is_null()) { - if (flags & ShaderWarning::UNUSED_CONSTANT) { - flags &= ~(ShaderWarning::UNUSED_CONSTANT); - } - if (flags & ShaderWarning::UNUSED_FUNCTION) { - flags &= ~(ShaderWarning::UNUSED_FUNCTION); - } - if (flags & ShaderWarning::UNUSED_STRUCT) { - flags &= ~(ShaderWarning::UNUSED_STRUCT); - } - if (flags & ShaderWarning::UNUSED_UNIFORM) { - flags &= ~(ShaderWarning::UNUSED_UNIFORM); - } - if (flags & ShaderWarning::UNUSED_VARYING) { - flags &= ~(ShaderWarning::UNUSED_VARYING); - } - } - sl.set_warning_flags(flags); - - ShaderLanguage::ShaderCompileInfo info; - info.global_shader_uniform_type_func = _get_global_shader_uniform_type; - - if (shader.is_null()) { - info.is_include = true; - } else { - Shader::Mode mode = shader->get_mode(); - info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode)); - info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode)); - info.shader_types = ShaderTypes::get_singleton()->get_types(); - } - - code = code_pp; - //compiler error - last_compile_result = sl.compile(code, info); - - if (last_compile_result != OK) { - String error_text; - int error_line; - Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions(); - if (include_positions.size() > 1) { - //error is in an include - error_line = include_positions[0].line; - error_text = "error(" + itos(error_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text(); - set_error_count(include_positions.size() - 1); - } else { - error_line = sl.get_error_line(); - error_text = "error(" + itos(error_line) + "): " + sl.get_error_text(); - set_error_count(0); - } - set_error(error_text); - set_error_pos(error_line - 1, 0); - get_text_editor()->set_line_background_color(error_line - 1, marked_line_color); - } else { - set_error(""); - } - - if (warnings.size() > 0 || last_compile_result != OK) { - warnings_panel->clear(); - } - warnings.clear(); - for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) { - warnings.push_back(E->get()); - } - if (warnings.size() > 0 && last_compile_result == OK) { - warnings.sort_custom<WarningsComparator>(); - _update_warning_panel(); - } else { - set_warning_count(0); - } - } - - emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts -} - -void ShaderTextEditor::_update_warning_panel() { - int warning_count = 0; - - warnings_panel->push_table(2); - for (int i = 0; i < warnings.size(); i++) { - ShaderWarning &w = warnings[i]; - - if (warning_count == 0) { - if (saved_treat_warning_as_errors) { - String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors."); - set_error_pos(w.get_line() - 1, 0); - set_error(error_text); - get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color); - } - } - - warning_count++; - int line = w.get_line(); - - // First cell. - warnings_panel->push_cell(); - warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor"))); - if (line != -1) { - warnings_panel->push_meta(line - 1); - warnings_panel->add_text(TTR("Line") + " " + itos(line)); - warnings_panel->add_text(" (" + w.get_name() + "):"); - warnings_panel->pop(); // Meta goto. - } else { - warnings_panel->add_text(w.get_name() + ":"); - } - warnings_panel->pop(); // Color. - warnings_panel->pop(); // Cell. - - // Second cell. - warnings_panel->push_cell(); - warnings_panel->add_text(w.get_message()); - warnings_panel->pop(); // Cell. - } - warnings_panel->pop(); // Table. - - set_warning_count(warning_count); -} - -void ShaderTextEditor::_bind_methods() { - ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid"))); -} - -ShaderTextEditor::ShaderTextEditor() { - syntax_highlighter.instantiate(); - get_text_editor()->set_syntax_highlighter(syntax_highlighter); -} - -/*** SCRIPT EDITOR ******/ - -void ShaderEditor::_menu_option(int p_option) { - switch (p_option) { - case EDIT_UNDO: { - shader_editor->get_text_editor()->undo(); - } break; - case EDIT_REDO: { - shader_editor->get_text_editor()->redo(); - } break; - case EDIT_CUT: { - shader_editor->get_text_editor()->cut(); - } break; - case EDIT_COPY: { - shader_editor->get_text_editor()->copy(); - } break; - case EDIT_PASTE: { - shader_editor->get_text_editor()->paste(); - } break; - case EDIT_SELECT_ALL: { - shader_editor->get_text_editor()->select_all(); - } break; - case EDIT_MOVE_LINE_UP: { - shader_editor->move_lines_up(); - } break; - case EDIT_MOVE_LINE_DOWN: { - shader_editor->move_lines_down(); - } break; - case EDIT_INDENT: { - if (shader.is_null()) { - return; - } - shader_editor->get_text_editor()->indent_lines(); - } break; - case EDIT_UNINDENT: { - if (shader.is_null()) { - return; - } - shader_editor->get_text_editor()->unindent_lines(); - } break; - case EDIT_DELETE_LINE: { - shader_editor->delete_lines(); - } break; - case EDIT_DUPLICATE_SELECTION: { - shader_editor->duplicate_selection(); - } break; - case EDIT_TOGGLE_COMMENT: { - if (shader.is_null()) { - return; - } - - shader_editor->toggle_inline_comment("//"); - - } break; - case EDIT_COMPLETE: { - shader_editor->get_text_editor()->request_code_completion(); - } break; - case SEARCH_FIND: { - shader_editor->get_find_replace_bar()->popup_search(); - } break; - case SEARCH_FIND_NEXT: { - shader_editor->get_find_replace_bar()->search_next(); - } break; - case SEARCH_FIND_PREV: { - shader_editor->get_find_replace_bar()->search_prev(); - } break; - case SEARCH_REPLACE: { - shader_editor->get_find_replace_bar()->popup_replace(); - } break; - case SEARCH_GOTO_LINE: { - goto_line_dialog->popup_find_line(shader_editor->get_text_editor()); - } break; - case BOOKMARK_TOGGLE: { - shader_editor->toggle_bookmark(); - } break; - case BOOKMARK_GOTO_NEXT: { - shader_editor->goto_next_bookmark(); - } break; - case BOOKMARK_GOTO_PREV: { - shader_editor->goto_prev_bookmark(); - } break; - case BOOKMARK_REMOVE_ALL: { - shader_editor->remove_all_bookmarks(); - } break; - case HELP_DOCS: { - OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL)); - } break; - } - if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) { - shader_editor->get_text_editor()->call_deferred(SNAME("grab_focus")); - } -} - -void ShaderEditor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_THEME_CHANGED: { - PopupMenu *popup = help_menu->get_popup(); - popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - } break; - - case NOTIFICATION_WM_WINDOW_FOCUS_IN: { - _check_for_external_edit(); - } break; - } -} - -void ShaderEditor::_editor_settings_changed() { - shader_editor->update_editor_settings(); - - shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/line_spacing")); - shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false); - shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false); -} - -void ShaderEditor::_show_warnings_panel(bool p_show) { - warnings_panel->set_visible(p_show); -} - -void ShaderEditor::_warning_clicked(Variant p_line) { - if (p_line.get_type() == Variant::INT) { - shader_editor->get_text_editor()->set_caret_line(p_line.operator int64_t()); - } -} - -void ShaderEditor::_bind_methods() { - ClassDB::bind_method("_show_warnings_panel", &ShaderEditor::_show_warnings_panel); - ClassDB::bind_method("_warning_clicked", &ShaderEditor::_warning_clicked); - - ADD_SIGNAL(MethodInfo("validation_changed")); -} - -void ShaderEditor::ensure_select_current() { -} - -void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) { - shader_editor->goto_line_selection(p_line, p_begin, p_end); -} - -void ShaderEditor::_project_settings_changed() { - _update_warnings(true); -} - -void ShaderEditor::_update_warnings(bool p_validate) { - bool changed = false; - - bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize(); - if (warnings_enabled != saved_warnings_enabled) { - saved_warnings_enabled = warnings_enabled; - changed = true; - } - - bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize(); - if (treat_warning_as_errors != saved_treat_warning_as_errors) { - saved_treat_warning_as_errors = treat_warning_as_errors; - changed = true; - } - - bool update_flags = false; - - for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) { - ShaderWarning::Code code = (ShaderWarning::Code)i; - bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower()); - - if (saved_warnings[code] != value) { - saved_warnings[code] = value; - update_flags = true; - changed = true; - } - } - - if (update_flags) { - saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings); - } - - if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) { - shader_editor->validate_script(); - } -} - -void ShaderEditor::_check_for_external_edit() { - bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change")); - - if (shader_inc.is_valid()) { - if (shader_inc->get_last_modified_time() != FileAccess::get_modified_time(shader_inc->get_path())) { - if (use_autoreload) { - _reload_shader_include_from_disk(); - } else { - disk_changed->call_deferred(SNAME("popup_centered")); - } - } - return; - } - - if (shader.is_null() || shader->is_built_in()) { - return; - } - - if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) { - if (use_autoreload) { - _reload_shader_from_disk(); - } else { - disk_changed->call_deferred(SNAME("popup_centered")); - } - } -} - -void ShaderEditor::_reload_shader_from_disk() { - Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); - ERR_FAIL_COND(!rel_shader.is_valid()); - - shader_editor->set_block_shader_changed(true); - shader->set_code(rel_shader->get_code()); - shader_editor->set_block_shader_changed(false); - shader->set_last_modified_time(rel_shader->get_last_modified_time()); - shader_editor->reload_text(); -} - -void ShaderEditor::_reload_shader_include_from_disk() { - Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); - ERR_FAIL_COND(!rel_shader_include.is_valid()); - - shader_editor->set_block_shader_changed(true); - shader_inc->set_code(rel_shader_include->get_code()); - shader_editor->set_block_shader_changed(false); - shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time()); - shader_editor->reload_text(); -} - -void ShaderEditor::_reload() { - if (shader.is_valid()) { - _reload_shader_from_disk(); - } else if (shader_inc.is_valid()) { - _reload_shader_include_from_disk(); - } -} - -void ShaderEditor::edit(const Ref<Shader> &p_shader) { - if (p_shader.is_null() || !p_shader->is_text_shader()) { - return; - } - - if (shader == p_shader) { - return; - } - - shader = p_shader; - shader_inc = Ref<ShaderInclude>(); - - shader_editor->set_edited_shader(shader); -} - -void ShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) { - if (p_shader_inc.is_null()) { - return; - } - - if (shader_inc == p_shader_inc) { - return; - } - - shader_inc = p_shader_inc; - shader = Ref<Shader>(); - - shader_editor->set_edited_shader_include(p_shader_inc); -} - -void ShaderEditor::save_external_data(const String &p_str) { - if (shader.is_null() && shader_inc.is_null()) { - disk_changed->hide(); - return; - } - - apply_shaders(); - - Ref<Shader> edited_shader = shader_editor->get_edited_shader(); - if (edited_shader.is_valid()) { - ResourceSaver::save(edited_shader); - } - if (shader.is_valid() && shader != edited_shader) { - ResourceSaver::save(shader); - } - - Ref<ShaderInclude> edited_shader_inc = shader_editor->get_edited_shader_include(); - if (edited_shader_inc.is_valid()) { - ResourceSaver::save(edited_shader_inc); - } - if (shader_inc.is_valid() && shader_inc != edited_shader_inc) { - ResourceSaver::save(shader_inc); - } - shader_editor->get_text_editor()->tag_saved_version(); - - disk_changed->hide(); -} - -void ShaderEditor::validate_script() { - shader_editor->_validate_script(); -} - -bool ShaderEditor::is_unsaved() const { - return shader_editor->get_text_editor()->get_saved_version() != shader_editor->get_text_editor()->get_version(); -} - -void ShaderEditor::apply_shaders() { - String editor_code = shader_editor->get_text_editor()->get_text(); - if (shader.is_valid()) { - String shader_code = shader->get_code(); - if (shader_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) { - shader_editor->set_block_shader_changed(true); - shader->set_code(editor_code); - shader_editor->set_block_shader_changed(false); - shader->set_edited(true); - } - } - if (shader_inc.is_valid()) { - String shader_inc_code = shader_inc->get_code(); - if (shader_inc_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) { - shader_editor->set_block_shader_changed(true); - shader_inc->set_code(editor_code); - shader_editor->set_block_shader_changed(false); - shader_inc->set_edited(true); - } - } - - dependencies_version = shader_editor->get_dependencies_version(); -} - -void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { - Ref<InputEventMouseButton> mb = ev; - - if (mb.is_valid()) { - if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { - CodeEdit *tx = shader_editor->get_text_editor(); - - Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position()); - int row = pos.y; - int col = pos.x; - tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click")); - - if (tx->is_move_caret_on_right_click_enabled()) { - if (tx->has_selection()) { - int from_line = tx->get_selection_from_line(); - int to_line = tx->get_selection_to_line(); - int from_column = tx->get_selection_from_column(); - int to_column = tx->get_selection_to_column(); - - if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) { - // Right click is outside the selected text - tx->deselect(); - } - } - if (!tx->has_selection()) { - tx->set_caret_line(row, true, false); - tx->set_caret_column(col); - } - } - _make_context_menu(tx->has_selection(), get_local_mouse_position()); - } - } - - Ref<InputEventKey> k = ev; - if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) { - CodeEdit *tx = shader_editor->get_text_editor(); - tx->adjust_viewport_to_caret(); - _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); - context_menu->grab_focus(); - } -} - -void ShaderEditor::_update_bookmark_list() { - bookmarks_menu->clear(); - - bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); - bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); - bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); - bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); - - PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines(); - if (bookmark_list.size() == 0) { - return; - } - - bookmarks_menu->add_separator(); - - for (int i = 0; i < bookmark_list.size(); i++) { - String line = shader_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges(); - // Limit the size of the line if too big. - if (line.length() > 50) { - line = line.substr(0, 50); - } - - bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\""); - bookmarks_menu->set_item_metadata(-1, bookmark_list[i]); - } -} - -void ShaderEditor::_bookmark_item_pressed(int p_idx) { - if (p_idx < 4) { // Any item before the separator. - _menu_option(bookmarks_menu->get_item_id(p_idx)); - } else { - shader_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx)); - } -} - -void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) { - context_menu->clear(); - if (p_selection) { - context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); - } - - context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); - context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); - - context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); - - context_menu->set_position(get_screen_position() + p_position); - context_menu->reset_size(); - context_menu->popup(); -} - -ShaderEditor::ShaderEditor() { - GLOBAL_DEF("debug/shader_language/warnings/enable", true); - GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false); - for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) { - GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true); - } - _update_warnings(false); - - shader_editor = memnew(ShaderTextEditor); - - shader_editor->connect("script_validated", callable_mp(this, &ShaderEditor::_script_validated)); - - shader_editor->set_v_size_flags(SIZE_EXPAND_FILL); - shader_editor->add_theme_constant_override("separation", 0); - shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - - shader_editor->connect("show_warnings_panel", callable_mp(this, &ShaderEditor::_show_warnings_panel)); - shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders)); - EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ShaderEditor::_editor_settings_changed)); - ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &ShaderEditor::_project_settings_changed)); - - shader_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line")); - - shader_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true); - shader_editor->get_text_editor()->set_context_menu_enabled(false); - shader_editor->get_text_editor()->connect("gui_input", callable_mp(this, &ShaderEditor::_text_edit_gui_input)); - - shader_editor->update_editor_settings(); - - context_menu = memnew(PopupMenu); - add_child(context_menu); - context_menu->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); - - VBoxContainer *main_container = memnew(VBoxContainer); - HBoxContainer *hbc = memnew(HBoxContainer); - - edit_menu = memnew(MenuButton); - edit_menu->set_shortcut_context(this); - edit_menu->set_text(TTR("Edit")); - edit_menu->set_switch_on_hover(true); - - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); - edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); - edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); - edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION); - edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); - edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); - - search_menu = memnew(MenuButton); - search_menu->set_shortcut_context(this); - search_menu->set_text(TTR("Search")); - search_menu->set_switch_on_hover(true); - - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); - search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); - - MenuButton *goto_menu = memnew(MenuButton); - goto_menu->set_shortcut_context(this); - goto_menu->set_text(TTR("Go To")); - goto_menu->set_switch_on_hover(true); - goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); - - goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); - goto_menu->get_popup()->add_separator(); - - bookmarks_menu = memnew(PopupMenu); - bookmarks_menu->set_name("Bookmarks"); - goto_menu->get_popup()->add_child(bookmarks_menu); - goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks"); - _update_bookmark_list(); - bookmarks_menu->connect("about_to_popup", callable_mp(this, &ShaderEditor::_update_bookmark_list)); - bookmarks_menu->connect("index_pressed", callable_mp(this, &ShaderEditor::_bookmark_item_pressed)); - - help_menu = memnew(MenuButton); - help_menu->set_text(TTR("Help")); - help_menu->set_switch_on_hover(true); - help_menu->get_popup()->add_item(TTR("Online Docs"), HELP_DOCS); - help_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); - - add_child(main_container); - main_container->add_child(hbc); - hbc->add_child(search_menu); - hbc->add_child(edit_menu); - hbc->add_child(goto_menu); - hbc->add_child(help_menu); - hbc->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditorPanel"), SNAME("EditorStyles"))); - - VSplitContainer *editor_box = memnew(VSplitContainer); - main_container->add_child(editor_box); - editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - editor_box->set_v_size_flags(SIZE_EXPAND_FILL); - editor_box->add_child(shader_editor); - - FindReplaceBar *bar = memnew(FindReplaceBar); - main_container->add_child(bar); - bar->hide(); - shader_editor->set_find_replace_bar(bar); - - warnings_panel = memnew(RichTextLabel); - warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); - warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL); - warnings_panel->set_meta_underline(true); - warnings_panel->set_selection_enabled(true); - warnings_panel->set_focus_mode(FOCUS_CLICK); - warnings_panel->hide(); - warnings_panel->connect("meta_clicked", callable_mp(this, &ShaderEditor::_warning_clicked)); - editor_box->add_child(warnings_panel); - shader_editor->set_warnings_panel(warnings_panel); - - goto_line_dialog = memnew(GotoLineDialog); - add_child(goto_line_dialog); - - disk_changed = memnew(ConfirmationDialog); - - VBoxContainer *vbc = memnew(VBoxContainer); - disk_changed->add_child(vbc); - - Label *dl = memnew(Label); - dl->set_text(TTR("This shader has been modified on disk.\nWhat action should be taken?")); - vbc->add_child(dl); - - disk_changed->connect("confirmed", callable_mp(this, &ShaderEditor::_reload)); - disk_changed->set_ok_button_text(TTR("Reload")); - - disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); - disk_changed->connect("custom_action", callable_mp(this, &ShaderEditor::save_external_data)); - - add_child(disk_changed); - - _editor_settings_changed(); -} void ShaderEditorPlugin::_update_shader_list() { shader_list->clear(); @@ -1241,7 +81,7 @@ void ShaderEditorPlugin::_update_shader_list() { shader_list->select(shader_tabs->get_current_tab()); } - for (int i = 1; i < FILE_MAX; i++) { + for (int i = FILE_SAVE; i < FILE_MAX; i++) { file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.size() == 0); } @@ -1250,7 +90,7 @@ void ShaderEditorPlugin::_update_shader_list() { void ShaderEditorPlugin::_update_shader_list_status() { for (int i = 0; i < shader_list->get_item_count(); i++) { - ShaderEditor *se = Object::cast_to<ShaderEditor>(shader_tabs->get_tab_control(i)); + TextShaderEditor *se = Object::cast_to<TextShaderEditor>(shader_tabs->get_tab_control(i)); if (se) { if (se->was_compilation_successful()) { shader_list->set_item_tag_icon(i, Ref<Texture2D>()); @@ -1285,7 +125,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { } } es.shader_inc = Ref<ShaderInclude>(si); - es.shader_editor = memnew(ShaderEditor); + es.shader_editor = memnew(TextShaderEditor); es.shader_editor->edit(si); shader_tabs->add_child(es.shader_editor); es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); @@ -1305,7 +145,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { shader_tabs->add_child(es.visual_shader_editor); es.visual_shader_editor->edit(vs.ptr()); } else { - es.shader_editor = memnew(ShaderEditor); + es.shader_editor = memnew(TextShaderEditor); shader_tabs->add_child(es.shader_editor); es.shader_editor->edit(s); es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); @@ -1330,7 +170,7 @@ void ShaderEditorPlugin::make_visible(bool p_visible) { void ShaderEditorPlugin::selected_notify() { } -ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { +TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { for (uint32_t i = 0; i < edited_shaders.size(); i++) { if (edited_shaders[i].shader == p_for_shader) { return edited_shaders[i].shader_editor; @@ -1592,7 +432,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() { file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed)); file_hb->add_child(file_menu); - for (int i = 2; i < FILE_MAX; i++) { + for (int i = FILE_SAVE; i < FILE_MAX; i++) { file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true); } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 5188639bfc..7638778af7 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -31,181 +31,14 @@ #ifndef SHADER_EDITOR_PLUGIN_H #define SHADER_EDITOR_PLUGIN_H -#include "editor/code_editor.h" #include "editor/editor_plugin.h" -#include "scene/gui/menu_button.h" -#include "scene/gui/panel_container.h" -#include "scene/gui/rich_text_label.h" -#include "scene/gui/tab_container.h" -#include "scene/gui/text_edit.h" -#include "scene/main/timer.h" -#include "scene/resources/shader.h" -#include "scene/resources/shader_include.h" -#include "servers/rendering/shader_warnings.h" -class ItemList; -class VisualShaderEditor; class HSplitContainer; +class ItemList; class ShaderCreateDialog; - -class GDShaderSyntaxHighlighter : public CodeHighlighter { - GDCLASS(GDShaderSyntaxHighlighter, CodeHighlighter) - -private: - Vector<Point2i> disabled_branch_regions; - Color disabled_branch_color; - -public: - virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override; - - void add_disabled_branch_region(const Point2i &p_region); - void clear_disabled_branch_regions(); - void set_disabled_branch_color(const Color &p_color); -}; - -class ShaderTextEditor : public CodeTextEditor { - GDCLASS(ShaderTextEditor, CodeTextEditor); - - Color marked_line_color = Color(1, 1, 1); - - struct WarningsComparator { - _ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); } - }; - - Ref<GDShaderSyntaxHighlighter> syntax_highlighter; - RichTextLabel *warnings_panel = nullptr; - Ref<Shader> shader; - Ref<ShaderInclude> shader_inc; - List<ShaderWarning> warnings; - Error last_compile_result = Error::OK; - - void _check_shader_mode(); - void _update_warning_panel(); - - bool block_shader_changed = false; - void _shader_changed(); - - uint32_t dependencies_version = 0; // Incremented if deps changed - -protected: - void _notification(int p_what); - static void _bind_methods(); - virtual void _load_theme_settings() override; - - virtual void _code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) override; - -public: - void set_block_shader_changed(bool p_block) { block_shader_changed = p_block; } - uint32_t get_dependencies_version() const { return dependencies_version; } - - virtual void _validate_script() override; - - void reload_text(); - void set_warnings_panel(RichTextLabel *p_warnings_panel); - - Ref<Shader> get_edited_shader() const; - Ref<ShaderInclude> get_edited_shader_include() const; - - void set_edited_shader(const Ref<Shader> &p_shader); - void set_edited_shader(const Ref<Shader> &p_shader, const String &p_code); - void set_edited_shader_include(const Ref<ShaderInclude> &p_include); - void set_edited_shader_include(const Ref<ShaderInclude> &p_include, const String &p_code); - void set_edited_code(const String &p_code); - - ShaderTextEditor(); -}; - -class ShaderEditor : public MarginContainer { - GDCLASS(ShaderEditor, MarginContainer); - - enum { - EDIT_UNDO, - EDIT_REDO, - EDIT_CUT, - EDIT_COPY, - EDIT_PASTE, - EDIT_SELECT_ALL, - EDIT_MOVE_LINE_UP, - EDIT_MOVE_LINE_DOWN, - EDIT_INDENT, - EDIT_UNINDENT, - EDIT_DELETE_LINE, - EDIT_DUPLICATE_SELECTION, - EDIT_TOGGLE_COMMENT, - EDIT_COMPLETE, - SEARCH_FIND, - SEARCH_FIND_NEXT, - SEARCH_FIND_PREV, - SEARCH_REPLACE, - SEARCH_GOTO_LINE, - BOOKMARK_TOGGLE, - BOOKMARK_GOTO_NEXT, - BOOKMARK_GOTO_PREV, - BOOKMARK_REMOVE_ALL, - HELP_DOCS, - }; - - MenuButton *edit_menu = nullptr; - MenuButton *search_menu = nullptr; - PopupMenu *bookmarks_menu = nullptr; - MenuButton *help_menu = nullptr; - PopupMenu *context_menu = nullptr; - RichTextLabel *warnings_panel = nullptr; - uint64_t idle = 0; - - GotoLineDialog *goto_line_dialog = nullptr; - ConfirmationDialog *erase_tab_confirm = nullptr; - ConfirmationDialog *disk_changed = nullptr; - - ShaderTextEditor *shader_editor = nullptr; - bool compilation_success = true; - - void _menu_option(int p_option); - mutable Ref<Shader> shader; - mutable Ref<ShaderInclude> shader_inc; - - void _editor_settings_changed(); - void _project_settings_changed(); - - void _check_for_external_edit(); - void _reload_shader_from_disk(); - void _reload_shader_include_from_disk(); - void _reload(); - void _show_warnings_panel(bool p_show); - void _warning_clicked(Variant p_line); - void _update_warnings(bool p_validate); - - void _script_validated(bool p_valid) { - compilation_success = p_valid; - emit_signal(SNAME("validation_changed")); - } - - uint32_t dependencies_version = 0xFFFFFFFF; - -protected: - void _notification(int p_what); - static void _bind_methods(); - void _make_context_menu(bool p_selection, Vector2 p_position); - void _text_edit_gui_input(const Ref<InputEvent> &p_ev); - - void _update_bookmark_list(); - void _bookmark_item_pressed(int p_idx); - -public: - bool was_compilation_successful() const { return compilation_success; } - void apply_shaders(); - void ensure_select_current(); - void edit(const Ref<Shader> &p_shader); - void edit(const Ref<ShaderInclude> &p_shader_inc); - void goto_line_selection(int p_line, int p_begin, int p_end); - void save_external_data(const String &p_str = ""); - void validate_script(); - bool is_unsaved() const; - - virtual Size2 get_minimum_size() const override { return Size2(0, 200); } - - ShaderEditor(); -}; +class TabContainer; +class TextShaderEditor; +class VisualShaderEditor; class ShaderEditorPlugin : public EditorPlugin { GDCLASS(ShaderEditorPlugin, EditorPlugin); @@ -213,12 +46,14 @@ class ShaderEditorPlugin : public EditorPlugin { struct EditedShader { Ref<Shader> shader; Ref<ShaderInclude> shader_inc; - ShaderEditor *shader_editor = nullptr; + TextShaderEditor *shader_editor = nullptr; VisualShaderEditor *visual_shader_editor = nullptr; }; LocalVector<EditedShader> edited_shaders; + // Always valid operations come first in the enum, file-specific ones + // should go after FILE_SAVE which is used to build the menu accordingly. enum { FILE_NEW, FILE_NEW_INCLUDE, @@ -265,7 +100,7 @@ public: virtual void make_visible(bool p_visible) override; virtual void selected_notify() override; - ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); + TextShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); VisualShaderEditor *get_visual_shader_editor(const Ref<Shader> &p_for_shader); virtual void save_external_data() override; diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp new file mode 100644 index 0000000000..dfd5e76ba6 --- /dev/null +++ b/editor/plugins/text_shader_editor.cpp @@ -0,0 +1,1191 @@ +/*************************************************************************/ +/* text_shader_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "text_shader_editor.h" + +#include "core/version_generated.gen.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "editor/filesystem_dock.h" +#include "editor/project_settings_editor.h" +#include "scene/gui/split_container.h" +#include "servers/rendering/shader_preprocessor.h" +#include "servers/rendering/shader_types.h" + +/*** SHADER SYNTAX HIGHLIGHTER ****/ + +Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) { + Dictionary color_map; + + for (const Point2i ®ion : disabled_branch_regions) { + if (p_line >= region.x && p_line <= region.y) { + Dictionary highlighter_info; + highlighter_info["color"] = disabled_branch_color; + + color_map[0] = highlighter_info; + return color_map; + } + } + + return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line); +} + +void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) { + ERR_FAIL_COND(p_region.x < 0); + ERR_FAIL_COND(p_region.y < 0); + + for (int i = 0; i < disabled_branch_regions.size(); i++) { + ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists."); + } + + Point2i disabled_branch_region; + disabled_branch_region.x = p_region.x; + disabled_branch_region.y = p_region.y; + disabled_branch_regions.push_back(disabled_branch_region); + + clear_highlighting_cache(); +} + +void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() { + disabled_branch_regions.clear(); + clear_highlighting_cache(); +} + +void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) { + disabled_branch_color = p_color; + clear_highlighting_cache(); +} + +/*** SHADER SCRIPT EDITOR ****/ + +static bool saved_warnings_enabled = false; +static bool saved_treat_warning_as_errors = false; +static HashMap<ShaderWarning::Code, bool> saved_warnings; +static uint32_t saved_warning_flags = 0U; + +void ShaderTextEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + if (is_visible_in_tree()) { + _load_theme_settings(); + if (warnings.size() > 0 && last_compile_result == OK) { + warnings_panel->clear(); + _update_warning_panel(); + } + } + } break; + } +} + +Ref<Shader> ShaderTextEditor::get_edited_shader() const { + return shader; +} + +Ref<ShaderInclude> ShaderTextEditor::get_edited_shader_include() const { + return shader_inc; +} + +void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) { + set_edited_shader(p_shader, p_shader->get_code()); +} + +void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const String &p_code) { + if (shader == p_shader) { + return; + } + if (shader.is_valid()) { + shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } + shader = p_shader; + shader_inc = Ref<ShaderInclude>(); + + set_edited_code(p_code); + + if (shader.is_valid()) { + shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } +} + +void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) { + set_edited_shader_include(p_shader_inc, p_shader_inc->get_code()); +} + +void ShaderTextEditor::_shader_changed() { + // This function is used for dependencies (include changing changes main shader and forces it to revalidate) + if (block_shader_changed) { + return; + } + dependencies_version++; + _validate_script(); +} + +void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) { + if (shader_inc == p_shader_inc) { + return; + } + if (shader_inc.is_valid()) { + shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } + shader_inc = p_shader_inc; + shader = Ref<Shader>(); + + set_edited_code(p_code); + + if (shader_inc.is_valid()) { + shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } +} + +void ShaderTextEditor::set_edited_code(const String &p_code) { + _load_theme_settings(); + + get_text_editor()->set_text(p_code); + get_text_editor()->clear_undo_history(); + get_text_editor()->call_deferred(SNAME("set_h_scroll"), 0); + get_text_editor()->call_deferred(SNAME("set_v_scroll"), 0); + get_text_editor()->tag_saved_version(); + + _validate_script(); + _line_col_changed(); +} + +void ShaderTextEditor::reload_text() { + ERR_FAIL_COND(shader.is_null()); + + CodeEdit *te = get_text_editor(); + int column = te->get_caret_column(); + int row = te->get_caret_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(shader->get_code()); + te->set_caret_line(row); + te->set_caret_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + + te->tag_saved_version(); + + update_line_and_column(); +} + +void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) { + warnings_panel = p_warnings_panel; +} + +void ShaderTextEditor::_load_theme_settings() { + CodeEdit *text_editor = get_text_editor(); + Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color"); + if (updated_marked_line_color != marked_line_color) { + for (int i = 0; i < text_editor->get_line_count(); i++) { + if (text_editor->get_line_background_color(i) == marked_line_color) { + text_editor->set_line_background_color(i, updated_marked_line_color); + } + } + marked_line_color = updated_marked_line_color; + } + + syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color")); + syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color")); + syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color")); + syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color")); + + syntax_highlighter->clear_keyword_colors(); + + const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color"); + const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color"); + + List<String> keywords; + ShaderLanguage::get_keyword_list(&keywords); + + for (const String &E : keywords) { + if (ShaderLanguage::is_control_flow_keyword(E)) { + syntax_highlighter->add_keyword_color(E, control_flow_keyword_color); + } else { + syntax_highlighter->add_keyword_color(E, keyword_color); + } + } + + List<String> pp_keywords; + ShaderPreprocessor::get_keyword_list(&pp_keywords, false); + + for (const String &E : pp_keywords) { + syntax_highlighter->add_keyword_color(E, keyword_color); + } + + // Colorize built-ins like `COLOR` differently to make them easier + // to distinguish from keywords at a quick glance. + + List<String> built_ins; + + if (shader_inc.is_valid()) { + for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { + for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) { + for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) { + built_ins.push_back(F.key); + } + } + + const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + + for (int j = 0; j < modes.size(); j++) { + const ShaderLanguage::ModeInfo &info = modes[j]; + + if (!info.options.is_empty()) { + for (int k = 0; k < info.options.size(); k++) { + built_ins.push_back(String(info.name) + "_" + String(info.options[k])); + } + } else { + built_ins.push_back(String(info.name)); + } + } + } + } else if (shader.is_valid()) { + for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) { + for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) { + built_ins.push_back(F.key); + } + } + + const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + + for (int i = 0; i < modes.size(); i++) { + const ShaderLanguage::ModeInfo &info = modes[i]; + + if (!info.options.is_empty()) { + for (int j = 0; j < info.options.size(); j++) { + built_ins.push_back(String(info.name) + "_" + String(info.options[j])); + } + } else { + built_ins.push_back(String(info.name)); + } + } + } + + const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color"); + + for (const String &E : built_ins) { + syntax_highlighter->add_keyword_color(E, user_type_color); + } + + // Colorize comments. + const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); + syntax_highlighter->clear_color_regions(); + syntax_highlighter->add_color_region("/*", "*/", comment_color, false); + syntax_highlighter->add_color_region("//", "", comment_color, true); + syntax_highlighter->set_disabled_branch_color(comment_color); + + text_editor->clear_comment_delimiters(); + text_editor->add_comment_delimiter("/*", "*/", false); + text_editor->add_comment_delimiter("//", "", true); + + if (!text_editor->has_auto_brace_completion_open_key("/*")) { + text_editor->add_auto_brace_completion_pair("/*", "*/"); + } + + // Colorize preprocessor include strings. + const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); + syntax_highlighter->add_color_region("\"", "\"", string_color, false); + + if (warnings_panel) { + // Warnings panel. + warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("main"), SNAME("EditorFonts"))); + warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts"))); + } +} + +void ShaderTextEditor::_check_shader_mode() { + String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text()); + + Shader::Mode mode; + + if (type == "canvas_item") { + mode = Shader::MODE_CANVAS_ITEM; + } else if (type == "particles") { + mode = Shader::MODE_PARTICLES; + } else if (type == "sky") { + mode = Shader::MODE_SKY; + } else if (type == "fog") { + mode = Shader::MODE_FOG; + } else { + mode = Shader::MODE_SPATIAL; + } + + if (shader->get_mode() != mode) { + set_block_shader_changed(true); + shader->set_code(get_text_editor()->get_text()); + set_block_shader_changed(false); + _load_theme_settings(); + } +} + +static ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_variable) { + RS::GlobalShaderParameterType gvt = RS::get_singleton()->global_shader_parameter_get_type(p_variable); + return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt); +} + +static String complete_from_path; + +static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) { + if (!p_efsd) { + return; + } + for (int i = 0; i < p_efsd->get_file_count(); i++) { + if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) { + String path = p_efsd->get_file_path(i); + if (path.begins_with(complete_from_path)) { + path = path.replace_first(complete_from_path, ""); + } + r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH)); + } + } + for (int j = 0; j < p_efsd->get_subdir_count(); j++) { + _complete_include_paths_search(p_efsd->get_subdir(j), r_options); + } +} + +static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) { + _complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options); +} + +void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) { + List<ScriptLanguage::CodeCompletionOption> pp_options; + List<ScriptLanguage::CodeCompletionOption> pp_defines; + ShaderPreprocessor preprocessor; + String code; + complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir(); + if (!complete_from_path.ends_with("/")) { + complete_from_path += "/"; + } + preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths); + complete_from_path = String(); + if (pp_options.size()) { + for (const ScriptLanguage::CodeCompletionOption &E : pp_options) { + r_options->push_back(E); + } + return; + } + for (const ScriptLanguage::CodeCompletionOption &E : pp_defines) { + r_options->push_back(E); + } + + ShaderLanguage sl; + String calltip; + ShaderLanguage::ShaderCompileInfo info; + info.global_shader_uniform_type_func = _get_global_shader_uniform_type; + + if (shader.is_null()) { + info.is_include = true; + + sl.complete(code, info, r_options, calltip); + get_text_editor()->set_code_hint(calltip); + return; + } + _check_shader_mode(); + info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())); + info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + info.shader_types = ShaderTypes::get_singleton()->get_types(); + + sl.complete(code, info, r_options, calltip); + get_text_editor()->set_code_hint(calltip); +} + +void ShaderTextEditor::_validate_script() { + emit_signal(SNAME("script_changed")); // Ensure to notify that it changed, so it is applied + + String code; + + if (shader.is_valid()) { + _check_shader_mode(); + code = shader->get_code(); + } else { + code = shader_inc->get_code(); + } + + ShaderPreprocessor preprocessor; + String code_pp; + String error_pp; + List<ShaderPreprocessor::FilePosition> err_positions; + List<ShaderPreprocessor::Region> regions; + String filename; + if (shader.is_valid()) { + filename = shader->get_path(); + } else if (shader_inc.is_valid()) { + filename = shader_inc->get_path(); + } + last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, ®ions); + + for (int i = 0; i < get_text_editor()->get_line_count(); i++) { + get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); + } + + syntax_highlighter->clear_disabled_branch_regions(); + for (const ShaderPreprocessor::Region ®ion : regions) { + if (!region.enabled) { + if (filename != region.file) { + continue; + } + syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line)); + } + } + + set_error(""); + set_error_count(0); + + if (last_compile_result != OK) { + //preprocessor error + ERR_FAIL_COND(err_positions.size() == 0); + + String error_text = error_pp; + int error_line = err_positions.front()->get().line; + if (err_positions.size() == 1) { + // Error in main file + error_text = "error(" + itos(error_line) + "): " + error_text; + } else { + error_text = "error(" + itos(error_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + error_text; + set_error_count(err_positions.size() - 1); + } + + set_error(error_text); + set_error_pos(error_line - 1, 0); + for (int i = 0; i < get_text_editor()->get_line_count(); i++) { + get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); + } + get_text_editor()->set_line_background_color(error_line - 1, marked_line_color); + + set_warning_count(0); + + } else { + ShaderLanguage sl; + + sl.enable_warning_checking(saved_warnings_enabled); + uint32_t flags = saved_warning_flags; + if (shader.is_null()) { + if (flags & ShaderWarning::UNUSED_CONSTANT) { + flags &= ~(ShaderWarning::UNUSED_CONSTANT); + } + if (flags & ShaderWarning::UNUSED_FUNCTION) { + flags &= ~(ShaderWarning::UNUSED_FUNCTION); + } + if (flags & ShaderWarning::UNUSED_STRUCT) { + flags &= ~(ShaderWarning::UNUSED_STRUCT); + } + if (flags & ShaderWarning::UNUSED_UNIFORM) { + flags &= ~(ShaderWarning::UNUSED_UNIFORM); + } + if (flags & ShaderWarning::UNUSED_VARYING) { + flags &= ~(ShaderWarning::UNUSED_VARYING); + } + } + sl.set_warning_flags(flags); + + ShaderLanguage::ShaderCompileInfo info; + info.global_shader_uniform_type_func = _get_global_shader_uniform_type; + + if (shader.is_null()) { + info.is_include = true; + } else { + Shader::Mode mode = shader->get_mode(); + info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode)); + info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode)); + info.shader_types = ShaderTypes::get_singleton()->get_types(); + } + + code = code_pp; + //compiler error + last_compile_result = sl.compile(code, info); + + if (last_compile_result != OK) { + String error_text; + int error_line; + Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions(); + if (include_positions.size() > 1) { + //error is in an include + error_line = include_positions[0].line; + error_text = "error(" + itos(error_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text(); + set_error_count(include_positions.size() - 1); + } else { + error_line = sl.get_error_line(); + error_text = "error(" + itos(error_line) + "): " + sl.get_error_text(); + set_error_count(0); + } + set_error(error_text); + set_error_pos(error_line - 1, 0); + get_text_editor()->set_line_background_color(error_line - 1, marked_line_color); + } else { + set_error(""); + } + + if (warnings.size() > 0 || last_compile_result != OK) { + warnings_panel->clear(); + } + warnings.clear(); + for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) { + warnings.push_back(E->get()); + } + if (warnings.size() > 0 && last_compile_result == OK) { + warnings.sort_custom<WarningsComparator>(); + _update_warning_panel(); + } else { + set_warning_count(0); + } + } + + emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts +} + +void ShaderTextEditor::_update_warning_panel() { + int warning_count = 0; + + warnings_panel->push_table(2); + for (int i = 0; i < warnings.size(); i++) { + ShaderWarning &w = warnings[i]; + + if (warning_count == 0) { + if (saved_treat_warning_as_errors) { + String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors."); + set_error_pos(w.get_line() - 1, 0); + set_error(error_text); + get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color); + } + } + + warning_count++; + int line = w.get_line(); + + // First cell. + warnings_panel->push_cell(); + warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + if (line != -1) { + warnings_panel->push_meta(line - 1); + warnings_panel->add_text(TTR("Line") + " " + itos(line)); + warnings_panel->add_text(" (" + w.get_name() + "):"); + warnings_panel->pop(); // Meta goto. + } else { + warnings_panel->add_text(w.get_name() + ":"); + } + warnings_panel->pop(); // Color. + warnings_panel->pop(); // Cell. + + // Second cell. + warnings_panel->push_cell(); + warnings_panel->add_text(w.get_message()); + warnings_panel->pop(); // Cell. + } + warnings_panel->pop(); // Table. + + set_warning_count(warning_count); +} + +void ShaderTextEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid"))); +} + +ShaderTextEditor::ShaderTextEditor() { + syntax_highlighter.instantiate(); + get_text_editor()->set_syntax_highlighter(syntax_highlighter); +} + +/*** SCRIPT EDITOR ******/ + +void TextShaderEditor::_menu_option(int p_option) { + switch (p_option) { + case EDIT_UNDO: { + shader_editor->get_text_editor()->undo(); + } break; + case EDIT_REDO: { + shader_editor->get_text_editor()->redo(); + } break; + case EDIT_CUT: { + shader_editor->get_text_editor()->cut(); + } break; + case EDIT_COPY: { + shader_editor->get_text_editor()->copy(); + } break; + case EDIT_PASTE: { + shader_editor->get_text_editor()->paste(); + } break; + case EDIT_SELECT_ALL: { + shader_editor->get_text_editor()->select_all(); + } break; + case EDIT_MOVE_LINE_UP: { + shader_editor->move_lines_up(); + } break; + case EDIT_MOVE_LINE_DOWN: { + shader_editor->move_lines_down(); + } break; + case EDIT_INDENT: { + if (shader.is_null()) { + return; + } + shader_editor->get_text_editor()->indent_lines(); + } break; + case EDIT_UNINDENT: { + if (shader.is_null()) { + return; + } + shader_editor->get_text_editor()->unindent_lines(); + } break; + case EDIT_DELETE_LINE: { + shader_editor->delete_lines(); + } break; + case EDIT_DUPLICATE_SELECTION: { + shader_editor->duplicate_selection(); + } break; + case EDIT_TOGGLE_COMMENT: { + if (shader.is_null()) { + return; + } + + shader_editor->toggle_inline_comment("//"); + + } break; + case EDIT_COMPLETE: { + shader_editor->get_text_editor()->request_code_completion(); + } break; + case SEARCH_FIND: { + shader_editor->get_find_replace_bar()->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + shader_editor->get_find_replace_bar()->search_next(); + } break; + case SEARCH_FIND_PREV: { + shader_editor->get_find_replace_bar()->search_prev(); + } break; + case SEARCH_REPLACE: { + shader_editor->get_find_replace_bar()->popup_replace(); + } break; + case SEARCH_GOTO_LINE: { + goto_line_dialog->popup_find_line(shader_editor->get_text_editor()); + } break; + case BOOKMARK_TOGGLE: { + shader_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + shader_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + shader_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + shader_editor->remove_all_bookmarks(); + } break; + case HELP_DOCS: { + OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL)); + } break; + } + if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) { + shader_editor->get_text_editor()->call_deferred(SNAME("grab_focus")); + } +} + +void TextShaderEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + PopupMenu *popup = help_menu->get_popup(); + popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + } break; + + case NOTIFICATION_WM_WINDOW_FOCUS_IN: { + _check_for_external_edit(); + } break; + } +} + +void TextShaderEditor::_editor_settings_changed() { + shader_editor->update_editor_settings(); + + shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/line_spacing")); + shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false); + shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false); +} + +void TextShaderEditor::_show_warnings_panel(bool p_show) { + warnings_panel->set_visible(p_show); +} + +void TextShaderEditor::_warning_clicked(Variant p_line) { + if (p_line.get_type() == Variant::INT) { + shader_editor->get_text_editor()->set_caret_line(p_line.operator int64_t()); + } +} + +void TextShaderEditor::_bind_methods() { + ClassDB::bind_method("_show_warnings_panel", &TextShaderEditor::_show_warnings_panel); + ClassDB::bind_method("_warning_clicked", &TextShaderEditor::_warning_clicked); + + ADD_SIGNAL(MethodInfo("validation_changed")); +} + +void TextShaderEditor::ensure_select_current() { +} + +void TextShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) { + shader_editor->goto_line_selection(p_line, p_begin, p_end); +} + +void TextShaderEditor::_project_settings_changed() { + _update_warnings(true); +} + +void TextShaderEditor::_update_warnings(bool p_validate) { + bool changed = false; + + bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize(); + if (warnings_enabled != saved_warnings_enabled) { + saved_warnings_enabled = warnings_enabled; + changed = true; + } + + bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize(); + if (treat_warning_as_errors != saved_treat_warning_as_errors) { + saved_treat_warning_as_errors = treat_warning_as_errors; + changed = true; + } + + bool update_flags = false; + + for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) { + ShaderWarning::Code code = (ShaderWarning::Code)i; + bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower()); + + if (saved_warnings[code] != value) { + saved_warnings[code] = value; + update_flags = true; + changed = true; + } + } + + if (update_flags) { + saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings); + } + + if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) { + shader_editor->validate_script(); + } +} + +void TextShaderEditor::_check_for_external_edit() { + bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change")); + + if (shader_inc.is_valid()) { + if (shader_inc->get_last_modified_time() != FileAccess::get_modified_time(shader_inc->get_path())) { + if (use_autoreload) { + _reload_shader_include_from_disk(); + } else { + disk_changed->call_deferred(SNAME("popup_centered")); + } + } + return; + } + + if (shader.is_null() || shader->is_built_in()) { + return; + } + + if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) { + if (use_autoreload) { + _reload_shader_from_disk(); + } else { + disk_changed->call_deferred(SNAME("popup_centered")); + } + } +} + +void TextShaderEditor::_reload_shader_from_disk() { + Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); + ERR_FAIL_COND(!rel_shader.is_valid()); + + shader_editor->set_block_shader_changed(true); + shader->set_code(rel_shader->get_code()); + shader_editor->set_block_shader_changed(false); + shader->set_last_modified_time(rel_shader->get_last_modified_time()); + shader_editor->reload_text(); +} + +void TextShaderEditor::_reload_shader_include_from_disk() { + Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); + ERR_FAIL_COND(!rel_shader_include.is_valid()); + + shader_editor->set_block_shader_changed(true); + shader_inc->set_code(rel_shader_include->get_code()); + shader_editor->set_block_shader_changed(false); + shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time()); + shader_editor->reload_text(); +} + +void TextShaderEditor::_reload() { + if (shader.is_valid()) { + _reload_shader_from_disk(); + } else if (shader_inc.is_valid()) { + _reload_shader_include_from_disk(); + } +} + +void TextShaderEditor::edit(const Ref<Shader> &p_shader) { + if (p_shader.is_null() || !p_shader->is_text_shader()) { + return; + } + + if (shader == p_shader) { + return; + } + + shader = p_shader; + shader_inc = Ref<ShaderInclude>(); + + shader_editor->set_edited_shader(shader); +} + +void TextShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) { + if (p_shader_inc.is_null()) { + return; + } + + if (shader_inc == p_shader_inc) { + return; + } + + shader_inc = p_shader_inc; + shader = Ref<Shader>(); + + shader_editor->set_edited_shader_include(p_shader_inc); +} + +void TextShaderEditor::save_external_data(const String &p_str) { + if (shader.is_null() && shader_inc.is_null()) { + disk_changed->hide(); + return; + } + + apply_shaders(); + + Ref<Shader> edited_shader = shader_editor->get_edited_shader(); + if (edited_shader.is_valid()) { + ResourceSaver::save(edited_shader); + } + if (shader.is_valid() && shader != edited_shader) { + ResourceSaver::save(shader); + } + + Ref<ShaderInclude> edited_shader_inc = shader_editor->get_edited_shader_include(); + if (edited_shader_inc.is_valid()) { + ResourceSaver::save(edited_shader_inc); + } + if (shader_inc.is_valid() && shader_inc != edited_shader_inc) { + ResourceSaver::save(shader_inc); + } + shader_editor->get_text_editor()->tag_saved_version(); + + disk_changed->hide(); +} + +void TextShaderEditor::validate_script() { + shader_editor->_validate_script(); +} + +bool TextShaderEditor::is_unsaved() const { + return shader_editor->get_text_editor()->get_saved_version() != shader_editor->get_text_editor()->get_version(); +} + +void TextShaderEditor::apply_shaders() { + String editor_code = shader_editor->get_text_editor()->get_text(); + if (shader.is_valid()) { + String shader_code = shader->get_code(); + if (shader_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) { + shader_editor->set_block_shader_changed(true); + shader->set_code(editor_code); + shader_editor->set_block_shader_changed(false); + shader->set_edited(true); + } + } + if (shader_inc.is_valid()) { + String shader_inc_code = shader_inc->get_code(); + if (shader_inc_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) { + shader_editor->set_block_shader_changed(true); + shader_inc->set_code(editor_code); + shader_editor->set_block_shader_changed(false); + shader_inc->set_edited(true); + } + } + + dependencies_version = shader_editor->get_dependencies_version(); +} + +void TextShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { + Ref<InputEventMouseButton> mb = ev; + + if (mb.is_valid()) { + if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { + CodeEdit *tx = shader_editor->get_text_editor(); + + Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position()); + int row = pos.y; + int col = pos.x; + tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click")); + + if (tx->is_move_caret_on_right_click_enabled()) { + if (tx->has_selection()) { + int from_line = tx->get_selection_from_line(); + int to_line = tx->get_selection_to_line(); + int from_column = tx->get_selection_from_column(); + int to_column = tx->get_selection_to_column(); + + if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) { + // Right click is outside the selected text + tx->deselect(); + } + } + if (!tx->has_selection()) { + tx->set_caret_line(row, true, false); + tx->set_caret_column(col); + } + } + _make_context_menu(tx->has_selection(), get_local_mouse_position()); + } + } + + Ref<InputEventKey> k = ev; + if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) { + CodeEdit *tx = shader_editor->get_text_editor(); + tx->adjust_viewport_to_caret(); + _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); + context_menu->grab_focus(); + } +} + +void TextShaderEditor::_update_bookmark_list() { + bookmarks_menu->clear(); + + bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + + PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines(); + if (bookmark_list.size() == 0) { + return; + } + + bookmarks_menu->add_separator(); + + for (int i = 0; i < bookmark_list.size(); i++) { + String line = shader_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges(); + // Limit the size of the line if too big. + if (line.length() > 50) { + line = line.substr(0, 50); + } + + bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\""); + bookmarks_menu->set_item_metadata(-1, bookmark_list[i]); + } +} + +void TextShaderEditor::_bookmark_item_pressed(int p_idx) { + if (p_idx < 4) { // Any item before the separator. + _menu_option(bookmarks_menu->get_item_id(p_idx)); + } else { + shader_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx)); + } +} + +void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) { + context_menu->clear(); + if (p_selection) { + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + } + + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); + + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + + context_menu->set_position(get_screen_position() + p_position); + context_menu->reset_size(); + context_menu->popup(); +} + +TextShaderEditor::TextShaderEditor() { + GLOBAL_DEF("debug/shader_language/warnings/enable", true); + GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false); + for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) { + GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true); + } + _update_warnings(false); + + shader_editor = memnew(ShaderTextEditor); + + shader_editor->connect("script_validated", callable_mp(this, &TextShaderEditor::_script_validated)); + + shader_editor->set_v_size_flags(SIZE_EXPAND_FILL); + shader_editor->add_theme_constant_override("separation", 0); + shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + + shader_editor->connect("show_warnings_panel", callable_mp(this, &TextShaderEditor::_show_warnings_panel)); + shader_editor->connect("script_changed", callable_mp(this, &TextShaderEditor::apply_shaders)); + EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextShaderEditor::_editor_settings_changed)); + ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &TextShaderEditor::_project_settings_changed)); + + shader_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line")); + + shader_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true); + shader_editor->get_text_editor()->set_context_menu_enabled(false); + shader_editor->get_text_editor()->connect("gui_input", callable_mp(this, &TextShaderEditor::_text_edit_gui_input)); + + shader_editor->update_editor_settings(); + + context_menu = memnew(PopupMenu); + add_child(context_menu); + context_menu->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option)); + + VBoxContainer *main_container = memnew(VBoxContainer); + HBoxContainer *hbc = memnew(HBoxContainer); + + edit_menu = memnew(MenuButton); + edit_menu->set_shortcut_context(this); + edit_menu->set_text(TTR("Edit")); + edit_menu->set_switch_on_hover(true); + + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); + edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option)); + + search_menu = memnew(MenuButton); + search_menu->set_shortcut_context(this); + search_menu->set_text(TTR("Search")); + search_menu->set_switch_on_hover(true); + + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); + search_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option)); + + MenuButton *goto_menu = memnew(MenuButton); + goto_menu->set_shortcut_context(this); + goto_menu->set_text(TTR("Go To")); + goto_menu->set_switch_on_hover(true); + goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option)); + + goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); + goto_menu->get_popup()->add_separator(); + + bookmarks_menu = memnew(PopupMenu); + bookmarks_menu->set_name("Bookmarks"); + goto_menu->get_popup()->add_child(bookmarks_menu); + goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks"); + _update_bookmark_list(); + bookmarks_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_update_bookmark_list)); + bookmarks_menu->connect("index_pressed", callable_mp(this, &TextShaderEditor::_bookmark_item_pressed)); + + help_menu = memnew(MenuButton); + help_menu->set_text(TTR("Help")); + help_menu->set_switch_on_hover(true); + help_menu->get_popup()->add_item(TTR("Online Docs"), HELP_DOCS); + help_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option)); + + add_child(main_container); + main_container->add_child(hbc); + hbc->add_child(search_menu); + hbc->add_child(edit_menu); + hbc->add_child(goto_menu); + hbc->add_child(help_menu); + hbc->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditorPanel"), SNAME("EditorStyles"))); + + VSplitContainer *editor_box = memnew(VSplitContainer); + main_container->add_child(editor_box); + editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + editor_box->set_v_size_flags(SIZE_EXPAND_FILL); + editor_box->add_child(shader_editor); + + FindReplaceBar *bar = memnew(FindReplaceBar); + main_container->add_child(bar); + bar->hide(); + shader_editor->set_find_replace_bar(bar); + + warnings_panel = memnew(RichTextLabel); + warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); + warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL); + warnings_panel->set_meta_underline(true); + warnings_panel->set_selection_enabled(true); + warnings_panel->set_focus_mode(FOCUS_CLICK); + warnings_panel->hide(); + warnings_panel->connect("meta_clicked", callable_mp(this, &TextShaderEditor::_warning_clicked)); + editor_box->add_child(warnings_panel); + shader_editor->set_warnings_panel(warnings_panel); + + goto_line_dialog = memnew(GotoLineDialog); + add_child(goto_line_dialog); + + disk_changed = memnew(ConfirmationDialog); + + VBoxContainer *vbc = memnew(VBoxContainer); + disk_changed->add_child(vbc); + + Label *dl = memnew(Label); + dl->set_text(TTR("This shader has been modified on disk.\nWhat action should be taken?")); + vbc->add_child(dl); + + disk_changed->connect("confirmed", callable_mp(this, &TextShaderEditor::_reload)); + disk_changed->set_ok_button_text(TTR("Reload")); + + disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->connect("custom_action", callable_mp(this, &TextShaderEditor::save_external_data)); + + add_child(disk_changed); + + _editor_settings_changed(); +} diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h new file mode 100644 index 0000000000..abeaff1fff --- /dev/null +++ b/editor/plugins/text_shader_editor.h @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* text_shader_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 TEXT_SHADER_EDITOR_H +#define TEXT_SHADER_EDITOR_H + +#include "editor/code_editor.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/rich_text_label.h" +#include "servers/rendering/shader_warnings.h" + +class GDShaderSyntaxHighlighter : public CodeHighlighter { + GDCLASS(GDShaderSyntaxHighlighter, CodeHighlighter) + +private: + Vector<Point2i> disabled_branch_regions; + Color disabled_branch_color; + +public: + virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override; + + void add_disabled_branch_region(const Point2i &p_region); + void clear_disabled_branch_regions(); + void set_disabled_branch_color(const Color &p_color); +}; + +class ShaderTextEditor : public CodeTextEditor { + GDCLASS(ShaderTextEditor, CodeTextEditor); + + Color marked_line_color = Color(1, 1, 1); + + struct WarningsComparator { + _ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); } + }; + + Ref<GDShaderSyntaxHighlighter> syntax_highlighter; + RichTextLabel *warnings_panel = nullptr; + Ref<Shader> shader; + Ref<ShaderInclude> shader_inc; + List<ShaderWarning> warnings; + Error last_compile_result = Error::OK; + + void _check_shader_mode(); + void _update_warning_panel(); + + bool block_shader_changed = false; + void _shader_changed(); + + uint32_t dependencies_version = 0; // Incremented if deps changed + +protected: + void _notification(int p_what); + static void _bind_methods(); + virtual void _load_theme_settings() override; + + virtual void _code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) override; + +public: + void set_block_shader_changed(bool p_block) { block_shader_changed = p_block; } + uint32_t get_dependencies_version() const { return dependencies_version; } + + virtual void _validate_script() override; + + void reload_text(); + void set_warnings_panel(RichTextLabel *p_warnings_panel); + + Ref<Shader> get_edited_shader() const; + Ref<ShaderInclude> get_edited_shader_include() const; + + void set_edited_shader(const Ref<Shader> &p_shader); + void set_edited_shader(const Ref<Shader> &p_shader, const String &p_code); + void set_edited_shader_include(const Ref<ShaderInclude> &p_include); + void set_edited_shader_include(const Ref<ShaderInclude> &p_include, const String &p_code); + void set_edited_code(const String &p_code); + + ShaderTextEditor(); +}; + +class TextShaderEditor : public MarginContainer { + GDCLASS(TextShaderEditor, MarginContainer); + + enum { + EDIT_UNDO, + EDIT_REDO, + EDIT_CUT, + EDIT_COPY, + EDIT_PASTE, + EDIT_SELECT_ALL, + EDIT_MOVE_LINE_UP, + EDIT_MOVE_LINE_DOWN, + EDIT_INDENT, + EDIT_UNINDENT, + EDIT_DELETE_LINE, + EDIT_DUPLICATE_SELECTION, + EDIT_TOGGLE_COMMENT, + EDIT_COMPLETE, + SEARCH_FIND, + SEARCH_FIND_NEXT, + SEARCH_FIND_PREV, + SEARCH_REPLACE, + SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, + HELP_DOCS, + }; + + MenuButton *edit_menu = nullptr; + MenuButton *search_menu = nullptr; + PopupMenu *bookmarks_menu = nullptr; + MenuButton *help_menu = nullptr; + PopupMenu *context_menu = nullptr; + RichTextLabel *warnings_panel = nullptr; + uint64_t idle = 0; + + GotoLineDialog *goto_line_dialog = nullptr; + ConfirmationDialog *erase_tab_confirm = nullptr; + ConfirmationDialog *disk_changed = nullptr; + + ShaderTextEditor *shader_editor = nullptr; + bool compilation_success = true; + + void _menu_option(int p_option); + mutable Ref<Shader> shader; + mutable Ref<ShaderInclude> shader_inc; + + void _editor_settings_changed(); + void _project_settings_changed(); + + void _check_for_external_edit(); + void _reload_shader_from_disk(); + void _reload_shader_include_from_disk(); + void _reload(); + void _show_warnings_panel(bool p_show); + void _warning_clicked(Variant p_line); + void _update_warnings(bool p_validate); + + void _script_validated(bool p_valid) { + compilation_success = p_valid; + emit_signal(SNAME("validation_changed")); + } + + uint32_t dependencies_version = 0xFFFFFFFF; + +protected: + void _notification(int p_what); + static void _bind_methods(); + void _make_context_menu(bool p_selection, Vector2 p_position); + void _text_edit_gui_input(const Ref<InputEvent> &p_ev); + + void _update_bookmark_list(); + void _bookmark_item_pressed(int p_idx); + +public: + bool was_compilation_successful() const { return compilation_success; } + void apply_shaders(); + void ensure_select_current(); + void edit(const Ref<Shader> &p_shader); + void edit(const Ref<ShaderInclude> &p_shader_inc); + void goto_line_selection(int p_line, int p_begin, int p_end); + void save_external_data(const String &p_str = ""); + void validate_script(); + bool is_unsaved() const; + + virtual Size2 get_minimum_size() const override { return Size2(0, 200); } + + TextShaderEditor(); +}; + +#endif // TEXT_SHADER_EDITOR_H diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 395c9b0248..acfa8b3d00 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2428,20 +2428,16 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i return HashMap<Vector2i, TileMapCell>(); } - if (selected_type == SELECTED_TYPE_CONNECT) { - return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true); - } else if (selected_type == SELECTED_TYPE_PATH) { - return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false); - } else { // SELECTED_TYPE_PATTERN - TileSet::TerrainsPattern terrains_pattern; - if (p_erase) { - terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - } else { - terrains_pattern = selected_terrains_pattern; + if (p_erase) { + return _draw_terrain_pattern(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set)); + } else { + if (selected_type == SELECTED_TYPE_CONNECT) { + return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true); + } else if (selected_type == SELECTED_TYPE_PATH) { + return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false); + } else { // SELECTED_TYPE_PATTERN + return _draw_terrain_pattern(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrains_pattern); } - - Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell); - return _draw_terrain_pattern(line, selected_terrain_set, terrains_pattern); } } @@ -2468,16 +2464,14 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i } } - if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) { - return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true); - } else { // SELECTED_TYPE_PATTERN - TileSet::TerrainsPattern terrains_pattern; - if (p_erase) { - terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - } else { - terrains_pattern = selected_terrains_pattern; + if (p_erase) { + return _draw_terrain_pattern(to_draw, selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set)); + } else { + if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) { + return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true); + } else { // SELECTED_TYPE_PATTERN + return _draw_terrain_pattern(to_draw, selected_terrain_set, selected_terrains_pattern); } - return _draw_terrain_pattern(to_draw, selected_terrain_set, terrains_pattern); } } @@ -2609,16 +2603,14 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Ve cells_to_draw_as_vector.append(cell); } - if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) { - return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true); - } else { // SELECTED_TYPE_PATTERN - TileSet::TerrainsPattern terrains_pattern; - if (p_erase) { - terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - } else { - terrains_pattern = selected_terrains_pattern; + if (p_erase) { + return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set)); + } else { + if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) { + return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true); + } else { // SELECTED_TYPE_PATTERN + return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, selected_terrains_pattern); } - return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, terrains_pattern); } } diff --git a/methods.py b/methods.py index dadac37cb5..7feffb2848 100644 --- a/methods.py +++ b/methods.py @@ -1,4 +1,5 @@ import os +import sys import re import glob import subprocess diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index e0deea1106..8b27307d0c 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -43,26 +43,28 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l String prev_text = ""; int prev_column = 0; - bool prev_is_char = false; bool prev_is_digit = false; bool prev_is_binary_op = false; + bool in_keyword = false; bool in_word = false; bool in_number = false; - bool in_function_name = false; - bool in_lambda = false; - bool in_variable_declaration = false; - bool in_signal_declaration = false; - bool in_function_args = false; - bool in_member_variable = false; bool in_node_path = false; bool in_node_ref = false; bool in_annotation = false; bool in_string_name = false; bool is_hex_notation = false; bool is_bin_notation = false; + bool in_member_variable = false; + bool in_lambda = false; + + bool in_function_name = false; + bool in_function_args = false; + bool in_variable_declaration = false; + bool in_signal_declaration = false; bool expect_type = false; + Color keyword_color; Color color; @@ -241,16 +243,21 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } } - // A bit of a hack, but couldn't come up with anything better. + // VERY hacky... but couldn't come up with anything better. if (j > 0 && (str[j] == '&' || str[j] == '^' || str[j] == '%' || str[j] == '+' || str[j] == '-' || str[j] == '~' || str[j] == '.')) { - if (prev_text == "true" || prev_text == "false" || prev_text == "self" || prev_text == "null" || prev_text == "PI" || prev_text == "TAU" || prev_text == "INF" || prev_text == "NAN") { - is_binary_op = true; - } else if (!keywords.has(prev_text)) { - int k = j - 1; - while (k > 0 && is_whitespace(str[k])) { - k--; - } - if (!is_symbol(str[k]) || str[k] == '"' || str[k] == '\'' || str[k] == ')' || str[k] == ']' || str[k] == '}') { + int to = j - 1; + // Find what the last text was (prev_text won't work if there's no whitespace, so we need to do it manually). + while (to > 0 && is_whitespace(str[to])) { + to--; + } + int from = to; + while (from > 0 && !is_symbol(str[from])) { + from--; + } + String word = str.substr(from + 1, to - from); + // Keywords need to be exceptions, except for keywords that represent a value. + if (word == "true" || word == "false" || word == "null" || word == "PI" || word == "TAU" || word == "INF" || word == "NAN" || word == "self" || word == "super" || !keywords.has(word)) { + if (!is_symbol(str[to]) || str[to] == '"' || str[to] == '\'' || str[to] == ')' || str[to] == ']' || str[to] == '}') { is_binary_op = true; } } @@ -285,16 +292,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l is_hex_notation = true; } else if (!((str[j] == '-' || str[j] == '+') && str[j - 1] == 'e' && !prev_is_digit) && !(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) && - !((str[j] == 'e' || str[j] == '.') && (prev_is_digit || str[j - 1] == '_')) && + !(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) && + !(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) && !((str[j] == '-' || str[j] == '+' || str[j] == '~') && !prev_is_binary_op && str[j - 1] != 'e')) { - /* 1st row of condition: '+' or '-' after scientific notation; - 2nd row of condition: '_' as a numeric separator; - 3rd row of condition: Scientific notation 'e' and floating points; - 4th row of condition: Multiple unary operators. */ + /* This condition continues Number highlighting in special cases. + 1st row: '+' or '-' after scientific notation; + 2nd row: '_' as a numeric separator; + 3rd row: Scientific notation 'e' and floating points; + 4th row: Floating points inside the number, or leading if after a unary mathematical operator; + 5th row: Multiple unary mathematical operators */ in_number = false; } - } else if ((str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.')))) && !is_binary_op) { - // Start a number from unary mathematical operators and floating points, except for '..' + } else if (!is_binary_op && (str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.'))))) { in_number = true; } @@ -316,7 +325,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l Color col = Color(); if (global_functions.has(word)) { // "assert" and "preload" are reserved, so highlight even if not followed by a bracket. - if (word == "assert" || word == "preload") { + if (word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ASSERT) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::PRELOAD)) { col = global_function_color; } else { // For other global functions, check if followed by bracket. @@ -406,11 +415,11 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_function_args = false; } - if (expect_type && (prev_is_char || str[j] == '=')) { + if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[') { expect_type = false; } - if (j > 0 && str[j] == '>' && str[j - 1] == '-') { + if (j > 0 && str[j - 1] == '-' && str[j] == '>') { expect_type = true; } diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index ab52761e17..f79731dd22 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -261,7 +261,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li #define ADD_OPTION_ENUM(PATH, ENUM_HINT, VALUE) \ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, SNAME(PATH), PROPERTY_HINT_ENUM, ENUM_HINT), VALUE)); - ADD_OPTION_ENUM("blender/nodes/visible", "Visible Only,Renderable,All", BLEND_VISIBLE_ALL); + ADD_OPTION_ENUM("blender/nodes/visible", "All,Visible Only,Renderable", BLEND_VISIBLE_ALL); ADD_OPTION_BOOL("blender/nodes/punctual_lights", true); ADD_OPTION_BOOL("blender/nodes/cameras", true); ADD_OPTION_BOOL("blender/nodes/custom_properties", true); diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h index dd1c1b9889..a1485ff82e 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.h +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -45,9 +45,9 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter { public: enum { + BLEND_VISIBLE_ALL, BLEND_VISIBLE_VISIBLE_ONLY, - BLEND_VISIBLE_RENDERABLE, - BLEND_VISIBLE_ALL + BLEND_VISIBLE_RENDERABLE }; enum { BLEND_BONE_INFLUENCES_NONE, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index fbd59d649f..9c3bc51c44 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -4,6 +4,21 @@ using System.Runtime.InteropServices; namespace Godot { /// <summary> + /// Specifies which order Euler angle rotations should be in. + /// When composing, the order is the same as the letters. When decomposing, + /// the order is reversed (ex: YXZ decomposes Z first, then X, and Y last). + /// </summary> + public enum EulerOrder + { + XYZ, + XZY, + YXZ, + YZX, + ZXY, + ZYX + }; + + /// <summary> /// 3×3 matrix used for 3D rotation and scale. /// Almost always used as an orthogonal basis for a Transform. /// @@ -244,50 +259,258 @@ namespace Godot } /// <summary> - /// Returns the basis's rotation in the form of Euler angles - /// (in the YXZ convention: when *decomposing*, first Z, then X, and Y last). - /// The returned vector contains the rotation angles in - /// the format (X angle, Y angle, Z angle). + /// Returns the basis's rotation in the form of Euler angles. + /// The Euler order depends on the [param order] parameter, + /// by default it uses the YXZ convention: when decomposing, + /// first Z, then X, and Y last. The returned vector contains + /// the rotation angles in the format (X angle, Y angle, Z angle). /// /// Consider using the <see cref="GetRotationQuaternion"/> method instead, which /// returns a <see cref="Quaternion"/> quaternion instead of Euler angles. /// </summary> + /// <param name="order">The Euler order to use. By default, use YXZ order (most common).</param> /// <returns>A <see cref="Vector3"/> representing the basis rotation in Euler angles.</returns> - public Vector3 GetEuler() + public Vector3 GetEuler(EulerOrder order = EulerOrder.YXZ) { - Basis m = Orthonormalized(); - - Vector3 euler; - euler.z = 0.0f; - - real_t mzy = m.Row1[2]; - - if (mzy < 1.0f) + switch (order) { - if (mzy > -1.0f) + case EulerOrder.XYZ: { - euler.x = Mathf.Asin(-mzy); - euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]); - euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]); + // Euler angles in XYZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + Vector3 euler; + real_t sy = Row0[2]; + if (sy < (1.0f - Mathf.Epsilon)) + { + if (sy > -(1.0f - Mathf.Epsilon)) + { + // is this a pure Y rotation? + if (Row1[0] == 0 && Row0[1] == 0 && Row1[2] == 0 && Row2[1] == 0 && Row1[1] == 1) + { + // return the simplest form (human friendlier in editor and scripts) + euler.x = 0; + euler.y = Mathf.Atan2(Row0[2], Row0[0]); + euler.z = 0; + } + else + { + euler.x = Mathf.Atan2(-Row1[2], Row2[2]); + euler.y = Mathf.Asin(sy); + euler.z = Mathf.Atan2(-Row0[1], Row0[0]); + } + } + else + { + euler.x = Mathf.Atan2(Row2[1], Row1[1]); + euler.y = -Mathf.Tau / 4.0f; + euler.z = 0.0f; + } + } + else + { + euler.x = Mathf.Atan2(Row2[1], Row1[1]); + euler.y = Mathf.Tau / 4.0f; + euler.z = 0.0f; + } + return euler; } - else + case EulerOrder.XZY: { - euler.x = Mathf.Pi * 0.5f; - euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]); + // Euler angles in XZY convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy -sz cz*sy + // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx + // cy*sx*sz cz*sx cx*cy+sx*sz*sy + Vector3 euler; + real_t sz = Row0[1]; + if (sz < (1.0f - Mathf.Epsilon)) + { + if (sz > -(1.0f - Mathf.Epsilon)) + { + euler.x = Mathf.Atan2(Row2[1], Row1[1]); + euler.y = Mathf.Atan2(Row0[2], Row0[0]); + euler.z = Mathf.Asin(-sz); + } + else + { + // It's -1 + euler.x = -Mathf.Atan2(Row1[2], Row2[2]); + euler.y = 0.0f; + euler.z = Mathf.Tau / 4.0f; + } + } + else + { + // It's 1 + euler.x = -Mathf.Atan2(Row1[2], Row2[2]); + euler.y = 0.0f; + euler.z = -Mathf.Tau / 4.0f; + } + return euler; } - } - else - { - euler.x = -Mathf.Pi * 0.5f; - euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]); - } + case EulerOrder.YXZ: + { + // Euler angles in YXZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy + // cx*sz cx*cz -sx + // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx + Vector3 euler; + real_t m12 = Row1[2]; + if (m12 < (1 - Mathf.Epsilon)) + { + if (m12 > -(1 - Mathf.Epsilon)) + { + // is this a pure X rotation? + if (Row1[0] == 0 && Row0[1] == 0 && Row0[2] == 0 && Row2[0] == 0 && Row0[0] == 1) + { + // return the simplest form (human friendlier in editor and scripts) + euler.x = Mathf.Atan2(-m12, Row1[1]); + euler.y = 0; + euler.z = 0; + } + else + { + euler.x = Mathf.Asin(-m12); + euler.y = Mathf.Atan2(Row0[2], Row2[2]); + euler.z = Mathf.Atan2(Row1[0], Row1[1]); + } + } + else + { // m12 == -1 + euler.x = Mathf.Tau / 4.0f; + euler.y = Mathf.Atan2(Row0[1], Row0[0]); + euler.z = 0; + } + } + else + { // m12 == 1 + euler.x = -Mathf.Tau / 4.0f; + euler.y = -Mathf.Atan2(Row0[1], Row0[0]); + euler.z = 0; + } - return euler; + return euler; + } + case EulerOrder.YZX: + { + // Euler angles in YZX convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx + // sz cz*cx -cz*sx + // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx + Vector3 euler; + real_t sz = Row1[0]; + if (sz < (1.0f - Mathf.Epsilon)) + { + if (sz > -(1.0f - Mathf.Epsilon)) + { + euler.x = Mathf.Atan2(-Row1[2], Row1[1]); + euler.y = Mathf.Atan2(-Row2[0], Row0[0]); + euler.z = Mathf.Asin(sz); + } + else + { + // It's -1 + euler.x = Mathf.Atan2(Row2[1], Row2[2]); + euler.y = 0.0f; + euler.z = -Mathf.Tau / 4.0f; + } + } + else + { + // It's 1 + euler.x = Mathf.Atan2(Row2[1], Row2[2]); + euler.y = 0.0f; + euler.z = Mathf.Tau / 4.0f; + } + return euler; + } + case EulerOrder.ZXY: + { + // Euler angles in ZXY convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx + // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx + // -cx*sy sx cx*cy + Vector3 euler; + real_t sx = Row2[1]; + if (sx < (1.0f - Mathf.Epsilon)) + { + if (sx > -(1.0f - Mathf.Epsilon)) + { + euler.x = Mathf.Asin(sx); + euler.y = Mathf.Atan2(-Row2[0], Row2[2]); + euler.z = Mathf.Atan2(-Row0[1], Row1[1]); + } + else + { + // It's -1 + euler.x = -Mathf.Tau / 4.0f; + euler.y = Mathf.Atan2(Row0[2], Row0[0]); + euler.z = 0; + } + } + else + { + // It's 1 + euler.x = Mathf.Tau / 4.0f; + euler.y = Mathf.Atan2(Row0[2], Row0[0]); + euler.z = 0; + } + return euler; + } + case EulerOrder.ZYX: + { + // Euler angles in ZYX convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy + // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx + // -sy cy*sx cy*cx + Vector3 euler; + real_t sy = Row2[0]; + if (sy < (1.0f - Mathf.Epsilon)) + { + if (sy > -(1.0f - Mathf.Epsilon)) + { + euler.x = Mathf.Atan2(Row2[1], Row2[2]); + euler.y = Mathf.Asin(-sy); + euler.z = Mathf.Atan2(Row1[0], Row0[0]); + } + else + { + // It's -1 + euler.x = 0; + euler.y = Mathf.Tau / 4.0f; + euler.z = -Mathf.Atan2(Row0[1], Row1[1]); + } + } + else + { + // It's 1 + euler.x = 0; + euler.y = -Mathf.Tau / 4.0f; + euler.z = -Mathf.Atan2(Row0[1], Row1[1]); + } + return euler; + } + default: + throw new ArgumentOutOfRangeException(nameof(order)); + } } /// <summary> /// Returns the basis's rotation in the form of a quaternion. - /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in + /// See <see cref="GetEuler"/> if you need Euler angles, but keep in /// mind that quaternions should generally be preferred to Euler angles. /// </summary> /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns> @@ -712,35 +935,6 @@ namespace Godot } /// <summary> - /// Constructs a pure rotation basis matrix from the given Euler angles - /// (in the YXZ convention: when *composing*, first Y, then X, and Z last), - /// given in the vector format as (X angle, Y angle, Z angle). - /// - /// Consider using the <see cref="Basis(Quaternion)"/> constructor instead, which - /// uses a <see cref="Quaternion"/> quaternion instead of Euler angles. - /// </summary> - /// <param name="eulerYXZ">The Euler angles to create the basis from.</param> - public Basis(Vector3 eulerYXZ) - { - real_t c; - real_t s; - - c = Mathf.Cos(eulerYXZ.x); - s = Mathf.Sin(eulerYXZ.x); - var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c); - - c = Mathf.Cos(eulerYXZ.y); - s = Mathf.Sin(eulerYXZ.y); - var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c); - - c = Mathf.Cos(eulerYXZ.z); - s = Mathf.Sin(eulerYXZ.z); - var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1); - - this = ymat * xmat * zmat; - } - - /// <summary> /// Constructs a pure rotation basis matrix, rotated around the given <paramref name="axis"/> /// by <paramref name="angle"/> (in radians). The axis must be a normalized vector. /// </summary> @@ -800,6 +994,46 @@ namespace Godot } /// <summary> + /// Constructs a Basis matrix from Euler angles in the specified rotation order. By default, use YXZ order (most common). + /// </summary> + /// <param name="euler">The Euler angles to use.</param> + /// <param name="order">The order to compose the Euler angles.</param> + public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.YXZ) + { + real_t c, s; + + c = Mathf.Cos(euler.x); + s = Mathf.Sin(euler.x); + Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c)); + + c = Mathf.Cos(euler.y); + s = Mathf.Sin(euler.y); + Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c)); + + c = Mathf.Cos(euler.z); + s = Mathf.Sin(euler.z); + Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1)); + + switch (order) + { + case EulerOrder.XYZ: + return xmat * ymat * zmat; + case EulerOrder.XZY: + return xmat * zmat * ymat; + case EulerOrder.YXZ: + return ymat * xmat * zmat; + case EulerOrder.YZX: + return ymat * zmat * xmat; + case EulerOrder.ZXY: + return zmat * xmat * ymat; + case EulerOrder.ZYX: + return zmat * ymat * xmat; + default: + throw new ArgumentOutOfRangeException(nameof(order)); + } + } + + /// <summary> /// Constructs a pure scale basis matrix with no rotation or shearing. /// The scale values are set as the main diagonal of the matrix, /// and all of the other parts of the matrix are zero. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs index 1b7f5158fd..bdedd2e87a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs @@ -72,7 +72,7 @@ namespace Godot /// <param name="delegate">Delegate method that will be called.</param> public Callable(Delegate @delegate) { - _target = null; + _target = @delegate?.Target as Object; _method = null; _delegate = @delegate; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 140fc167ba..76b186cd15 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -721,8 +721,9 @@ namespace Godot.NativeInterop if (p_managed_callable.Delegate != null) { var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate); + IntPtr objectPtr = p_managed_callable.Target != null ? Object.GetPtr(p_managed_callable.Target) : IntPtr.Zero; NativeFuncs.godotsharp_callable_new_with_delegate( - GCHandle.ToIntPtr(gcHandle), out godot_callable callable); + GCHandle.ToIntPtr(gcHandle), objectPtr, out godot_callable callable); return callable; } else diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index bd00611383..20ede9f0dd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -141,7 +141,7 @@ namespace Godot.NativeInterop public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest, in godot_string p_element); - public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, + public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_object, out godot_callable r_callable); internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable, diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 276701cdaa..2717b945f6 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -447,9 +447,10 @@ void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String r_dest->append(*p_element); } -void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) { +void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, const Object *p_object, Callable *r_callable) { // TODO: Use pooling for ManagedCallable instances. - CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle)); + ObjectID objid = p_object ? p_object->get_instance_id() : ObjectID(); + CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, objid)); memnew_placement(r_callable, Callable(managed_callable)); } diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index 9305dc645a..0c2c533090 100644 --- a/modules/mono/managed_callable.cpp +++ b/modules/mono/managed_callable.cpp @@ -79,7 +79,9 @@ CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const { } ObjectID ManagedCallable::get_object() const { - // TODO: If the delegate target extends Godot.Object, use that instead! + if (object_id != ObjectID()) { + return object_id; + } return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id(); } @@ -104,7 +106,7 @@ void ManagedCallable::release_delegate_handle() { // Why you do this clang-format... /* clang-format off */ -ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) { +ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id) : delegate_handle(p_delegate_handle), object_id(p_object_id) { #ifdef GD_MONO_HOT_RELOAD { MutexLock lock(instances_mutex); diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h index aa3344f4d5..26cd164fb6 100644 --- a/modules/mono/managed_callable.h +++ b/modules/mono/managed_callable.h @@ -40,6 +40,7 @@ class ManagedCallable : public CallableCustom { friend class CSharpLanguage; GCHandleIntPtr delegate_handle; + ObjectID object_id; #ifdef GD_MONO_HOT_RELOAD SelfList<ManagedCallable> self_instance = this; @@ -66,7 +67,7 @@ public: void release_delegate_handle(); - ManagedCallable(GCHandleIntPtr p_delegate_handle); + ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id); ~ManagedCallable(); }; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 394c32f20d..83862e1e34 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -238,6 +238,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p to_visit.clear(); to_visit.push_back(0); least_cost_id = 0; + prev_least_cost_id = -1; reachable_end = nullptr; diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 66dea6cf1b..81fc941608 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -3142,6 +3142,11 @@ void DisplayServerX11::_window_changed(XEvent *event) { return; } + // Query display server about a possible new window state. + wd.fullscreen = _window_fullscreen_check(window_id); + wd.minimized = _window_minimize_check(window_id); + wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE"); + { //the position in xconfigure is not useful here, obtain it manually int x, y; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 549cc19cb1..05b4f181c2 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2799,7 +2799,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local } // Shortcut Input. - if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr) { + if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr || Object::cast_to<InputEventJoypadButton>(*ev) != nullptr) { get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index cde241f231..1e70f6880c 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -230,6 +230,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { float sh[9 * 4]; }; + // When changing any of these enums, remember to change the corresponding enums in the shader files as well. enum { INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 1b31d2749d..a53872fc88 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -371,7 +371,7 @@ protected: /* Geometry instance */ - // check which ones of these apply, probably all except GI and SDFGI + // When changing any of these enums, remember to change the corresponding enums in the shader files as well. enum { INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index e8e2dce990..0d36b98645 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -62,13 +62,14 @@ layout(set = 0, binding = 3) uniform sampler decal_sampler; layout(set = 0, binding = 4) uniform sampler light_projector_sampler; -#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 5) -#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6) -#define INSTANCE_FLAGS_USE_SDFGI (1 << 7) -#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8) -#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9) -#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10) -#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 11) +#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) +#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) +#define INSTANCE_FLAGS_USE_SDFGI (1 << 6) +#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7) +#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8) +#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9) +#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10) +#define INSTANCE_FLAGS_PARTICLES (1 << 11) #define INSTANCE_FLAGS_MULTIMESH (1 << 12) #define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13) #define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14) diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 5e4999fa0f..631ff0575b 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -55,13 +55,14 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler; layout(set = 0, binding = 3) uniform sampler decal_sampler; layout(set = 0, binding = 4) uniform sampler light_projector_sampler; -#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 5) -#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6) -#define INSTANCE_FLAGS_USE_SDFGI (1 << 7) -#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8) -#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9) -#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10) -#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 11) +#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) +#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) +#define INSTANCE_FLAGS_USE_SDFGI (1 << 6) +#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7) +#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8) +#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9) +#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10) +#define INSTANCE_FLAGS_PARTICLES (1 << 11) #define INSTANCE_FLAGS_MULTIMESH (1 << 12) #define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13) #define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14) diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h index 52118fa943..be271bad1f 100644 --- a/tests/core/math/test_vector3.h +++ b/tests/core/math/test_vector3.h @@ -84,16 +84,12 @@ TEST_CASE("[Vector3] Axis methods") { vector.min_axis_index() == Vector3::Axis::AXIS_X, "Vector3 min_axis_index should work as expected."); CHECK_MESSAGE( - vector.get_axis(vector.max_axis_index()) == (real_t)5.6, - "Vector3 get_axis should work as expected."); + vector[vector.max_axis_index()] == (real_t)5.6, + "Vector3 array operator should work as expected."); CHECK_MESSAGE( vector[vector.min_axis_index()] == (real_t)1.2, "Vector3 array operator should work as expected."); - vector.set_axis(Vector3::Axis::AXIS_Y, 4.7); - CHECK_MESSAGE( - vector.get_axis(Vector3::Axis::AXIS_Y) == (real_t)4.7, - "Vector3 set_axis should work as expected."); vector[Vector3::Axis::AXIS_Y] = 3.7; CHECK_MESSAGE( vector[Vector3::Axis::AXIS_Y] == (real_t)3.7, diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h index 6c52781556..2050b222d0 100644 --- a/tests/core/math/test_vector3i.h +++ b/tests/core/math/test_vector3i.h @@ -53,16 +53,12 @@ TEST_CASE("[Vector3i] Axis methods") { vector.min_axis_index() == Vector3i::Axis::AXIS_X, "Vector3i min_axis_index should work as expected."); CHECK_MESSAGE( - vector.get_axis(vector.max_axis_index()) == 3, - "Vector3i get_axis should work as expected."); + vector[vector.max_axis_index()] == 3, + "Vector3i array operator should work as expected."); CHECK_MESSAGE( vector[vector.min_axis_index()] == 1, "Vector3i array operator should work as expected."); - vector.set_axis(Vector3i::Axis::AXIS_Y, 4); - CHECK_MESSAGE( - vector.get_axis(Vector3i::Axis::AXIS_Y) == 4, - "Vector3i set_axis should work as expected."); vector[Vector3i::Axis::AXIS_Y] = 5; CHECK_MESSAGE( vector[Vector3i::Axis::AXIS_Y] == 5, diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h index 25ec8929b8..3f50f16635 100644 --- a/tests/core/math/test_vector4.h +++ b/tests/core/math/test_vector4.h @@ -55,16 +55,12 @@ TEST_CASE("[Vector4] Axis methods") { vector.min_axis_index() == Vector4::Axis::AXIS_W, "Vector4 min_axis_index should work as expected."); CHECK_MESSAGE( - vector.get_axis(vector.max_axis_index()) == (real_t)5.6, - "Vector4 get_axis should work as expected."); + vector[vector.max_axis_index()] == (real_t)5.6, + "Vector4 array operator should work as expected."); CHECK_MESSAGE( vector[vector.min_axis_index()] == (real_t)-0.9, "Vector4 array operator should work as expected."); - vector.set_axis(Vector4::Axis::AXIS_Y, 4.7); - CHECK_MESSAGE( - vector.get_axis(Vector4::Axis::AXIS_Y) == (real_t)4.7, - "Vector4 set_axis should work as expected."); vector[Vector4::Axis::AXIS_Y] = 3.7; CHECK_MESSAGE( vector[Vector4::Axis::AXIS_Y] == (real_t)3.7, diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h index e106099914..309162c3f7 100644 --- a/tests/core/math/test_vector4i.h +++ b/tests/core/math/test_vector4i.h @@ -53,16 +53,12 @@ TEST_CASE("[Vector4i] Axis methods") { vector.min_axis_index() == Vector4i::Axis::AXIS_X, "Vector4i min_axis_index should work as expected."); CHECK_MESSAGE( - vector.get_axis(vector.max_axis_index()) == 4, - "Vector4i get_axis should work as expected."); + vector[vector.max_axis_index()] == 4, + "Vector4i array operator should work as expected."); CHECK_MESSAGE( vector[vector.min_axis_index()] == 1, "Vector4i array operator should work as expected."); - vector.set_axis(Vector4i::Axis::AXIS_Y, 5); - CHECK_MESSAGE( - vector.get_axis(Vector4i::Axis::AXIS_Y) == 5, - "Vector4i set_axis should work as expected."); vector[Vector4i::Axis::AXIS_Y] = 5; CHECK_MESSAGE( vector[Vector4i::Axis::AXIS_Y] == 5, diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index d97da05c04..969f5fc096 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -740,6 +740,14 @@ TEST_CASE("[String] sprintf") { REQUIRE(error == false); CHECK(output == String("fish 99.990000 frog")); + // Real (infinity) left-padded + format = "fish %11f frog"; + args.clear(); + args.push_back(INFINITY); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish inf frog")); + // Real right-padded. format = "fish %-11f frog"; args.clear(); @@ -840,6 +848,14 @@ TEST_CASE("[String] sprintf") { REQUIRE(error == false); CHECK(output == String("fish ( 19.990000, 1.000000, -2.050000) frog")); + // Vector left-padded with inf/nan + format = "fish %11v frog"; + args.clear(); + args.push_back(Variant(Vector2(INFINITY, NAN))); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish ( inf, nan) frog")); + // Vector right-padded. format = "fish %-11v frog"; args.clear(); |