diff options
33 files changed, 540 insertions, 238 deletions
diff --git a/core/input/input.cpp b/core/input/input.cpp index beb61f039f..b3a68bb98c 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1409,8 +1409,8 @@ String Input::get_joy_guid(int p_device) const { return joy_names[p_device].uid; } -Array Input::get_connected_joypads() { - Array ret; +TypedArray<int> Input::get_connected_joypads() { + TypedArray<int> ret; HashMap<int, Joypad>::Iterator elem = joy_names.begin(); while (elem) { if (elem->value.connected) { diff --git a/core/input/input.h b/core/input/input.h index d65b585749..f02f2abae5 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -36,6 +36,7 @@ #include "core/os/keyboard.h" #include "core/os/thread_safe.h" #include "core/templates/rb_set.h" +#include "core/variant/typed_array.h" class Input : public Object { GDCLASS(Input, Object); @@ -259,7 +260,7 @@ public: float get_joy_axis(int p_device, JoyAxis p_axis) const; String get_joy_name(int p_idx); - Array get_connected_joypads(); + TypedArray<int> get_connected_joypads(); Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 44df349613..d189f3224b 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3712,18 +3712,15 @@ String String::uri_encode() const { const CharString temp = utf8(); String res; for (int i = 0; i < temp.length(); ++i) { - char ord = temp[i]; + uint8_t ord = temp[i]; if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) { res += ord; } else { - char h_Val[3]; -#if defined(__GNUC__) || defined(_MSC_VER) - snprintf(h_Val, 3, "%02hhX", ord); -#else - sprintf(h_Val, "%02hhX", ord); -#endif - res += "%"; - res += h_Val; + char p[4] = { '%', 0, 0, 0 }; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + p[1] = hex[ord >> 4]; + p[2] = hex[ord & 0xF]; + res += p; } } return res; diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index e69bd88413..2b4e777865 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3102,6 +3102,10 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const return *reinterpret_cast<const String *>(_data._mem) == *reinterpret_cast<const String *>(p_variant._data._mem); } break; + case STRING_NAME: { + return *reinterpret_cast<const StringName *>(_data._mem) == *reinterpret_cast<const StringName *>(p_variant._data._mem); + } break; + case VECTOR2: { const Vector2 *l = reinterpret_cast<const Vector2 *>(_data._mem); const Vector2 *r = reinterpret_cast<const Vector2 *>(p_variant._data._mem); diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index f841b66888..66683fa0ee 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -80,7 +80,7 @@ </description> </method> <method name="get_connected_joypads"> - <return type="Array" /> + <return type="int[]" /> <description> Returns an [Array] containing the device IDs of all currently connected joypads. </description> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index ee57d8f26b..0a2ceeedc5 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -80,6 +80,12 @@ Clears the array of polygons, but it doesn't clear the array of outlines and vertices. </description> </method> + <method name="get_mesh"> + <return type="NavigationMesh" /> + <description> + Returns the [NavigationMesh] resulting from this navigation polygon. This navmesh can be used to update the navmesh of a region with the [method NavigationServer3D.region_set_navmesh] API directly (as 2D uses the 3D server behind the scene). + </description> + </method> <method name="get_outline" qualifiers="const"> <return type="PackedVector2Array" /> <argument index="0" name="idx" type="int" /> diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index f2bb63e528..1b1cdbd9ef 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -86,7 +86,7 @@ void EditorProfiler::add_frame_metric(const Metric &p_metric, bool p_final) { void EditorProfiler::clear() { int metric_size = EditorSettings::get_singleton()->get("debugger/profiler_frame_history_size"); - metric_size = CLAMP(metric_size, 60, 1024); + metric_size = CLAMP(metric_size, 60, 10000); frame_metrics.clear(); frame_metrics.resize(metric_size); total_metrics = 0; @@ -660,7 +660,7 @@ EditorProfiler::EditorProfiler() { h_split->add_child(graph); graph->set_h_size_flags(SIZE_EXPAND_FILL); - int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 1024); + int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000); frame_metrics.resize(metric_size); EDITOR_DEF("debugger/profiler_frame_max_functions", 64); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 503c03bafe..9def646f3f 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -93,7 +93,7 @@ void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { void EditorVisualProfiler::clear() { int metric_size = EditorSettings::get_singleton()->get("debugger/profiler_frame_history_size"); - metric_size = CLAMP(metric_size, 60, 1024); + metric_size = CLAMP(metric_size, 60, 10000); frame_metrics.clear(); frame_metrics.resize(metric_size); last_metric = -1; @@ -789,7 +789,7 @@ EditorVisualProfiler::EditorVisualProfiler() { h_split->add_child(graph); graph->set_h_size_flags(SIZE_EXPAND_FILL); - int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 1024); + int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000); frame_metrics.resize(metric_size); frame_delay = memnew(Timer); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 196fba6e9b..e3d4f1cab0 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -733,14 +733,12 @@ void EditorPropertyFlags::_set_read_only(bool p_read_only) { } }; -void EditorPropertyFlags::_flag_toggled() { - uint32_t value = 0; - for (int i = 0; i < flags.size(); i++) { - if (flags[i]->is_pressed()) { - uint32_t val = 1; - val <<= flag_indices[i]; - value |= val; - } +void EditorPropertyFlags::_flag_toggled(int p_index) { + uint32_t value = get_edited_object()->get(get_edited_property()); + if (flags[p_index]->is_pressed()) { + value |= flag_values[p_index]; + } else { + value &= ~flag_values[p_index]; } emit_changed(get_edited_property(), value); @@ -750,13 +748,7 @@ void EditorPropertyFlags::update_property() { uint32_t value = get_edited_object()->get(get_edited_property()); for (int i = 0; i < flags.size(); i++) { - uint32_t val = 1; - val <<= flag_indices[i]; - if (value & val) { - flags[i]->set_pressed(true); - } else { - flags[i]->set_pressed(false); - } + flags[i]->set_pressed((value & flag_values[i]) == flag_values[i]); } } @@ -764,17 +756,24 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) { ERR_FAIL_COND(flags.size()); bool first = true; + uint32_t current_val; for (int i = 0; i < p_options.size(); i++) { String option = p_options[i].strip_edges(); if (!option.is_empty()) { CheckBox *cb = memnew(CheckBox); cb->set_text(option); cb->set_clip_text(true); - cb->connect("pressed", callable_mp(this, &EditorPropertyFlags::_flag_toggled)); + cb->connect("pressed", callable_mp(this, &EditorPropertyFlags::_flag_toggled), varray(i)); add_focusable(cb); vbox->add_child(cb); flags.push_back(cb); - flag_indices.push_back(i); + Vector<String> text_split = p_options[i].split(":"); + if (text_split.size() != 1) { + current_val = text_split[1].to_int(); + } else { + current_val = 1 << i; + } + flag_values.push_back(current_val); if (first) { set_label_reference(cb); first = false; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 5ee0ba1a6d..a3990db678 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -265,9 +265,9 @@ class EditorPropertyFlags : public EditorProperty { GDCLASS(EditorPropertyFlags, EditorProperty); VBoxContainer *vbox = nullptr; Vector<CheckBox *> flags; - Vector<int> flag_indices; + Vector<uint32_t> flag_values; - void _flag_toggled(); + void _flag_toggled(int p_index); protected: virtual void _set_read_only(bool p_read_only) override; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 504e09aa36..e682db69d1 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -715,7 +715,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "network/ssl/editor_ssl_certificates", _SYSTEM_CERTS_PATH, "*.crt,*.pem", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); // Profiler - _initial_set("debugger/profiler_frame_history_size", 600); + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_frame_history_size", 3600, "60,10000,1") // HTTP Proxy _initial_set("network/http_proxy/host", ""); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index c53c04af4e..fa106979be 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1484,6 +1484,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const int gn_margin_side = 2; const int gn_margin_bottom = 2; + // StateMachine + const int sm_margin_side = 10; + Color graphnode_bg = dark_color_3; if (!dark_theme) { graphnode_bg = prop_section_color; @@ -1510,10 +1513,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { graphsbposition->set_border_color(error_color); graphsbposition->set_shadow_color(error_color * Color(1.0, 1.0, 1.0, 0.2)); Ref<StyleBoxEmpty> graphsbslot = make_empty_stylebox(12, 0, 12, 0); - Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width); + Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width); smgraphsb->set_border_width_all(border_width); smgraphsb->set_border_color(graphnode_bg); - Ref<StyleBoxFlat> smgraphsbselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.9), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width); + Ref<StyleBoxFlat> smgraphsbselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.9), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width); smgraphsbselected->set_border_width_all(2 * EDSCALE + border_width); smgraphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); smgraphsbselected->set_shadow_size(8 * EDSCALE); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index bd9b89cbb7..ece0c6a0ce 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1220,7 +1220,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { for (const StringName &E : nodes) { Ref<AnimationNode> anode = state_machine->get_node(E); String name = E; - bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr()); + bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); Ref<StyleBox> sb = selected_nodes.has(E) ? style_selected : style; Size2 s = sb->get_minimum_size(); @@ -1432,7 +1432,6 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } else { state_machine_draw->draw_texture(edit, nr.edit.position); } - offset.x += sep + edit->get_width(); } } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index a0ca219c61..06ad0f8918 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -411,6 +411,11 @@ void Node3DEditorViewport::cancel_transform() { set_message(TTR("Transform Aborted."), 3); } +void Node3DEditorViewport::_update_shrink() { + bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); + subviewport_container->set_stretch_shrink(shrink ? 2 : 1); +} + float Node3DEditorViewport::get_znear() const { return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z); } @@ -2387,11 +2392,7 @@ void Node3DEditorViewport::_project_settings_changed() { viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2)); viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3)); - bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); - - if (shrink != (subviewport_container->get_stretch_shrink() > 1)) { - subviewport_container->set_stretch_shrink(shrink ? 2 : 1); - } + _update_shrink(); // Update MSAA, screen-space AA and debanding if changed @@ -3085,8 +3086,8 @@ void Node3DEditorViewport::_menu_option(int p_option) { case VIEW_HALF_RESOLUTION: { int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - view_menu->get_popup()->set_item_checked(idx, current); + view_menu->get_popup()->set_item_checked(idx, !current); + _update_shrink(); } break; case VIEW_INFORMATION: { int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 511135a5f1..627443bd62 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -251,6 +251,7 @@ private: Transform3D _get_camera_transform() const; int get_selected_count() const; void cancel_transform(); + void _update_shrink(); Vector3 _get_camera_position() const; Vector3 _get_camera_normal() const; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 6c39244f80..cd8aca2a8e 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -3428,6 +3428,7 @@ void TileMapEditor::_notification(int p_what) { void TileMapEditor::_on_grid_toggled(bool p_pressed) { EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); + CanvasItemEditor::get_singleton()->update_viewport(); } void TileMapEditor::_layers_selection_button_draw() { diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index da67ed79ba..771d34d841 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -105,15 +105,16 @@ void CustomPropertyEditor::_menu_option(int p_which) { switch (type) { case Variant::INT: { if (hint == PROPERTY_HINT_FLAGS) { - int val = v; - - if (val & (1 << p_which)) { - val &= ~(1 << p_which); + int idx = menu->get_item_index(p_which); + uint32_t item_value = menu->get_item_metadata(idx); + uint32_t value = v; + // If the item wasn't previously checked it means it was pressed, + // otherwise it was unpressed. + if (!menu->is_item_checked(idx)) { + v = value | item_value; } else { - val |= (1 << p_which); + v = value & ~item_value; } - - v = val; emit_signal(SNAME("variant_changed")); } else if (hint == PROPERTY_HINT_ENUM) { v = menu->get_item_metadata(p_which); @@ -486,15 +487,19 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: set_size(Size2(200, 150) * EDSCALE); } else if (hint == PROPERTY_HINT_FLAGS) { Vector<String> flags = hint_text.split(","); + uint32_t value = v; for (int i = 0; i < flags.size(); i++) { - String flag = flags[i]; - if (flag.is_empty()) { - continue; + uint32_t current_val; + Vector<String> text_split = flags[i].split(":"); + if (text_split.size() != 1) { + current_val = text_split[1].to_int(); + } else { + current_val = 1 << i; } - menu->add_check_item(flag, i); - int f = v; - if (f & (1 << i)) { - menu->set_item_checked(menu->get_item_index(i), true); + menu->add_check_item(text_split[0], current_val); + menu->set_item_metadata(i, current_val); + if ((value & current_val) == current_val) { + menu->set_item_checked(menu->get_item_index(current_val), true); } } menu->set_position(get_position()); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index bee4e0e1fb..622838b308 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2807,7 +2807,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage GD_MONO_ASSERT_THREAD_ATTACHED; if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) { - r_hint = PROPERTY_HINT_ENUM; + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM; Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields(); @@ -2844,7 +2845,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error); ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value."); - if (val != (unsigned int)i) { + unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i; + if (val != expected_val) { uses_default_values = false; } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index e602396ede..960d2fe27c 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -1652,7 +1652,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append("static "); } - p_output.append(prop_itype->cs_type); + String prop_cs_type = prop_itype->cs_type + _get_generic_type_parameters(*prop_itype, proptype_name.generic_type_parameters); + + p_output.append(prop_cs_type); p_output.append(" "); p_output.append(p_iprop.proxy_name); p_output.append("\n" INDENT2 OPEN_BLOCK); @@ -1762,6 +1764,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf "Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'."); } + String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters); + // Add the current arguments to the signature // If the argument has a default value which is not a constant, we will make it Nullable { @@ -1773,7 +1777,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf arguments_sig += "Nullable<"; } - arguments_sig += arg_type->cs_type; + arguments_sig += arg_cs_type; if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) { arguments_sig += "> "; @@ -1800,7 +1804,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String arg_in = iarg.name; arg_in += "_in"; - cs_in_statements += arg_type->cs_type; + cs_in_statements += arg_cs_type; cs_in_statements += " "; cs_in_statements += arg_in; cs_in_statements += " = "; @@ -1820,7 +1824,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf cs_in_statements += " : "; } - String cs_type = arg_type->cs_type; + String cs_type = arg_cs_type; if (cs_type.ends_with("[]")) { cs_type = cs_type.substr(0, cs_type.length() - 2); } @@ -1837,7 +1841,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Escape < and > in the attribute default value String param_def_arg = def_arg.replacen("<", "<").replacen(">", ">"); - default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + param_def_arg + "</param>"); + default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is <c>" + param_def_arg + "</c>.</param>"); } else { icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } @@ -1903,7 +1907,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf p_output.append("virtual "); } - p_output.append(return_type->cs_type + " "); + String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters); + + p_output.append(return_cs_type + " "); p_output.append(p_imethod.proxy_name + "("); p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2); @@ -1914,7 +1920,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf p_output.append("return;\n" CLOSE_BLOCK_L2); } else { p_output.append("return default("); - p_output.append(return_type->cs_type); + p_output.append(return_cs_type); p_output.append(");\n" CLOSE_BLOCK_L2); } @@ -1956,7 +1962,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } else if (return_type->cs_out.is_empty()) { p_output.append("return " + im_call + "(" + icall_params + ");\n"); } else { - p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out)); + p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out)); p_output.append("\n"); } @@ -2517,6 +2523,42 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol return &placeholder_types.insert(placeholder.cname, placeholder)->value; } +const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) { + if (p_generic_type_parameters.is_empty()) { + return ""; + } + + ERR_FAIL_COND_V_MSG(p_itype.type_parameter_count != p_generic_type_parameters.size(), "", + "Generic type parameter count mismatch for type '" + p_itype.name + "'." + + " Found " + itos(p_generic_type_parameters.size()) + ", but requires " + + itos(p_itype.type_parameter_count) + "."); + + int i = 0; + String params = "<"; + for (const TypeReference ¶m_type : p_generic_type_parameters) { + const TypeInterface *param_itype = _get_type_or_placeholder(param_type); + + ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "", + "Generic type parameter is a singleton: '" + param_itype->name + "'."); + + if (p_itype.api_type == ClassDB::API_CORE) { + ERR_FAIL_COND_V_MSG(param_itype->api_type == ClassDB::API_EDITOR, "", + "Generic type parameter '" + param_itype->name + "' has type from the editor API." + + " Core API cannot have dependencies on the editor API."); + } + + params += param_itype->cs_type; + if (i < p_generic_type_parameters.size() - 1) { + params += ", "; + } + + i++; + } + params += ">"; + + return params; +} + StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { switch (p_meta) { case GodotTypeInfo::METADATA_INT_IS_INT8: @@ -2832,6 +2874,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() { ERR_FAIL_COND_V_MSG(bad_reference_hint, false, String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." + " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'."); + } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) { + imethod.return_type.cname = Variant::get_type_name(return_info.type); + imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string)); } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { imethod.return_type.cname = return_info.hint_string; } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { @@ -2861,6 +2906,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() { iarg.type.is_enum = true; } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; + } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { + iarg.type.cname = Variant::get_type_name(arginfo.type); + iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { @@ -2966,6 +3014,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() { iarg.type.is_enum = true; } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; + } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { + iarg.type.cname = Variant::get_type_name(arginfo.type); + iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { @@ -3549,13 +3600,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Array"; itype.cname = itype.name; itype.proxy_name = itype.name; + itype.type_parameter_count = 1; itype.c_out = "\treturn memnew(Array(%1));\n"; itype.c_type = itype.name; itype.c_type_in = itype.c_type + "*"; itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new " + itype.cs_type + "(%0(%1));"; + itype.cs_out = "return new %2(%0(%1));"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); @@ -3565,13 +3617,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Dictionary"; itype.cname = itype.name; itype.proxy_name = itype.name; + itype.type_parameter_count = 2; itype.c_out = "\treturn memnew(Dictionary(%1));\n"; itype.c_type = itype.name; itype.c_type_in = itype.c_type + "*"; itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new " + itype.cs_type + "(%0(%1));"; + itype.cs_out = "return new %2(%0(%1));"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index fb7e0e5a81..f0ba2b18e4 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -87,6 +87,8 @@ class BindingsGenerator { StringName cname; bool is_enum = false; + List<TypeReference> generic_type_parameters; + TypeReference() {} TypeReference(const StringName &p_cname) : @@ -206,6 +208,8 @@ class BindingsGenerator { String name; StringName cname; + int type_parameter_count; + /** * Identifier name of the base class. */ @@ -679,6 +683,8 @@ class BindingsGenerator { const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); + const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters); + StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index ee4d0eed08..50ae2eb112 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -80,6 +80,11 @@ namespace Godot private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); /// <summary> + /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type. + /// </summary> + private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false); + + /// <summary> /// Returns the generic type definition of <paramref name="type"/>. /// </summary> /// <exception cref="InvalidOperationException"> diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index d8fd244067..44a8e26b8f 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -176,6 +176,7 @@ void CachedData::clear_godot_api_cache() { methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify(); methodthunk_MarshalUtils_TypeIsGenericICollection.nullify(); methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify(); + methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify(); methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify(); @@ -300,6 +301,7 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2)); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 4000342c94..92136e1f41 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -147,6 +147,7 @@ struct CachedData { GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable; GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection; GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 25678be624..1983d6ebe2 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -614,6 +614,14 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) { return (bool)res; } +bool type_has_flags_attribute(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); + MonoException *exc = nullptr; + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) { MonoException *exc = nullptr; CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 4c2c2c93c2..246a1cd31e 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -61,6 +61,7 @@ bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype); bool type_is_generic_ienumerable(MonoReflectionType *p_reftype); bool type_is_generic_icollection(MonoReflectionType *p_reftype); bool type_is_generic_idictionary(MonoReflectionType *p_reftype); +bool type_has_flags_attribute(MonoReflectionType *p_reftype); void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype); diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp index 684faf7c8e..4741c7f887 100644 --- a/modules/visual_script/editor/visual_script_editor.cpp +++ b/modules/visual_script/editor/visual_script_editor.cpp @@ -889,7 +889,49 @@ void VisualScriptEditor::_update_graph(int p_only_id) { EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { - button->set_text(pi.hint_string.get_slice(",", value)); + bool found = false; + const Vector<String> options = pi.hint_string.split(","); + int64_t current_val = 0; + for (const String &option : options) { + Vector<String> text_split = option.split(":"); + if (text_split.size() != 1) { + current_val = text_split[1].to_int(); + } + if (value.operator int() == current_val) { + button->set_text(text_split[0]); + found = true; + break; + } + current_val += 1; + } + if (!found) { + button->set_text(value); + } + } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_FLAGS) { + Vector<String> value_texts; + const Vector<String> options = pi.hint_string.split(","); + uint32_t v = value; + for (const String &option : options) { + uint32_t current_val; + Vector<String> text_split = option.split(":"); + if (text_split.size() != -1) { + current_val = text_split[1].to_int(); + } else { + current_val = 1 << i; + } + if ((v & current_val) == current_val) { + value_texts.push_back(text_split[0]); + } + } + if (value_texts.size() != 0) { + String value_text = value_texts[0]; + for (const String &text : value_texts) { + value_text += " | " + text; + } + button->set_text(value_text); + } else { + button->set_text(value); + } } else { button->set_text(value); } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 260faf1d68..41773d81c5 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -331,6 +331,7 @@ void NavigationPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("get_polygon_count"), &NavigationPolygon::get_polygon_count); ClassDB::bind_method(D_METHOD("get_polygon", "idx"), &NavigationPolygon::get_polygon); ClassDB::bind_method(D_METHOD("clear_polygons"), &NavigationPolygon::clear_polygons); + ClassDB::bind_method(D_METHOD("get_mesh"), &NavigationPolygon::get_mesh); ClassDB::bind_method(D_METHOD("add_outline", "outline"), &NavigationPolygon::add_outline); ClassDB::bind_method(D_METHOD("add_outline_at_index", "outline", "index"), &NavigationPolygon::add_outline_at_index); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 193f18c075..9b347ed7c8 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -681,7 +681,7 @@ void Window::_update_viewport_size() { } break; case CONTENT_SCALE_MODE_VIEWPORT: { - final_size = viewport_size; + final_size = (viewport_size / content_scale_factor).floor(); attach_to_screen_rect = Rect2(margin, screen_size); } break; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 8e2e9b14cb..096d371b8d 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -1150,7 +1150,7 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet p_textures[k++] = rd_texture; } } else { - bool srgb = p_use_linear_color && p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR; + bool srgb = p_use_linear_color && p_texture_uniforms[i].use_color; for (int j = 0; j < textures.size(); j++) { Texture *tex = TextureStorage::get_singleton()->get_texture(textures[j]); diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 81cd83aebb..463b67033d 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -571,6 +571,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene texture.name = uniform_name; texture.hint = uniform.hint; texture.type = uniform.type; + texture.use_color = uniform.use_color; texture.filter = uniform.filter; texture.repeat = uniform.repeat; texture.global = uniform.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL; diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index 2656ee68a6..06f42e9f0f 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -61,6 +61,7 @@ public: StringName name; ShaderLanguage::DataType type; ShaderLanguage::ShaderNode::Uniform::Hint hint; + bool use_color = false; ShaderLanguage::TextureFilter filter; ShaderLanguage::TextureRepeat repeat; bool global; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index b25296e3b0..fe3d1e03b6 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1014,6 +1014,93 @@ String ShaderLanguage::get_datatype_name(DataType p_type) { return ""; } +String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) { + String result; + switch (p_hint) { + case ShaderNode::Uniform::HINT_RANGE: { + result = "hint_range"; + } break; + case ShaderNode::Uniform::HINT_SOURCE_COLOR: { + result = "hint_color"; + } break; + case ShaderNode::Uniform::HINT_NORMAL: { + result = "hint_normal"; + } break; + case ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL: { + result = "hint_roughness_normal"; + } break; + case ShaderNode::Uniform::HINT_ROUGHNESS_R: { + result = "hint_roughness_r"; + } break; + case ShaderNode::Uniform::HINT_ROUGHNESS_G: { + result = "hint_roughness_g"; + } break; + case ShaderNode::Uniform::HINT_ROUGHNESS_B: { + result = "hint_roughness_b"; + } break; + case ShaderNode::Uniform::HINT_ROUGHNESS_A: { + result = "hint_roughness_a"; + } break; + case ShaderNode::Uniform::HINT_ROUGHNESS_GRAY: { + result = "hint_roughness_gray"; + } break; + case ShaderNode::Uniform::HINT_DEFAULT_BLACK: { + result = "hint_default_black"; + } break; + case ShaderNode::Uniform::HINT_DEFAULT_WHITE: { + result = "hint_default_white"; + } break; + case ShaderNode::Uniform::HINT_ANISOTROPY: { + result = "hint_anisotropy"; + } break; + default: + break; + } + return result; +} + +String ShaderLanguage::get_texture_filter_name(TextureFilter p_filter) { + String result; + switch (p_filter) { + case FILTER_NEAREST: { + result = "filter_nearest"; + } break; + case FILTER_LINEAR: { + result = "filter_linear"; + } break; + case FILTER_NEAREST_MIPMAP: { + result = "filter_nearest_mipmap"; + } break; + case FILTER_LINEAR_MIPMAP: { + result = "filter_linear_mipmap"; + } break; + case FILTER_NEAREST_MIPMAP_ANISOTROPIC: { + result = "filter_nearest_mipmap_anisotropic"; + } break; + case FILTER_LINEAR_MIPMAP_ANISOTROPIC: { + result = "filter_linear_mipmap_anisotropic"; + } break; + default: { + } break; + } + return result; +} + +String ShaderLanguage::get_texture_repeat_name(TextureRepeat p_repeat) { + String result; + switch (p_repeat) { + case REPEAT_DISABLE: { + result = "repeat_disable"; + } break; + case REPEAT_ENABLE: { + result = "repeat_enable"; + } break; + default: { + } break; + } + return result; +} + bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) { return is_token_datatype(p_type) && p_type != TK_TYPE_VOID; } @@ -7970,11 +8057,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f [[fallthrough]]; case TK_UNIFORM: case TK_VARYING: { - bool uniform = tk.type == TK_UNIFORM; + bool is_uniform = tk.type == TK_UNIFORM; #ifdef DEBUG_ENABLED keyword_completion_context = CF_UNSPECIFIED; #endif // DEBUG_ENABLED - if (!uniform) { + if (!is_uniform) { if (shader_type_identifier == "particles" || shader_type_identifier == "sky" || shader_type_identifier == "fog") { _set_error(vformat(RTR("Varyings cannot be used in '%s' shaders."), shader_type_identifier)); return ERR_PARSE_ERROR; @@ -7991,7 +8078,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f bool temp_error = false; uint32_t datatype_flag; - if (!uniform) { + if (!is_uniform) { datatype_flag = CF_VARYING_TYPE; keyword_completion_context = CF_INTERPOLATION_QUALIFIER | CF_PRECISION_MODIFIER | datatype_flag; @@ -8019,7 +8106,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f #endif // DEBUG_ENABLED if (is_token_interpolation(tk.type)) { - if (uniform) { + if (is_uniform) { _set_error(RTR("Interpolation qualifiers are not supported for uniforms.")); #ifdef DEBUG_ENABLED temp_error = true; @@ -8065,7 +8152,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f } if (shader->structs.has(tk.text)) { - if (uniform) { + if (is_uniform) { _set_error(vformat(RTR("The '%s' data type is not supported for uniforms."), "struct")); return ERR_PARSE_ERROR; } else { @@ -8090,7 +8177,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f return ERR_PARSE_ERROR; } - if (!uniform && (type < TYPE_FLOAT || type > TYPE_MAT4)) { + if (!is_uniform && (type < TYPE_FLOAT || type > TYPE_MAT4)) { _set_error(RTR("Invalid type for varying, only 'float', 'vec2', 'vec3', 'vec4', 'mat2', 'mat3', 'mat4', or arrays of these types are allowed.")); return ERR_PARSE_ERROR; } @@ -8131,7 +8218,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f return ERR_PARSE_ERROR; } - if (uniform) { + if (is_uniform) { if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL && Engine::get_singleton()->is_editor_hint()) { // Type checking for global uniforms is not allowed outside the editor. //validate global uniform DataType gvtype = global_var_get_type_func(name); @@ -8145,16 +8232,16 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f return ERR_PARSE_ERROR; } } - ShaderNode::Uniform uniform2; + ShaderNode::Uniform uniform; - uniform2.type = type; - uniform2.scope = uniform_scope; - uniform2.precision = precision; - uniform2.array_size = array_size; + uniform.type = type; + uniform.scope = uniform_scope; + uniform.precision = precision; + uniform.array_size = array_size; tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - Error error = _parse_array_size(nullptr, constants, true, nullptr, &uniform2.array_size, nullptr); + Error error = _parse_array_size(nullptr, constants, true, nullptr, &uniform.array_size, nullptr); if (error != OK) { return error; } @@ -8166,14 +8253,14 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f _set_error(vformat(RTR("The '%s' qualifier is not supported for sampler types."), "SCOPE_INSTANCE")); return ERR_PARSE_ERROR; } - uniform2.texture_order = texture_uniforms++; - uniform2.texture_binding = texture_binding; - if (uniform2.array_size > 0) { - texture_binding += uniform2.array_size; + uniform.texture_order = texture_uniforms++; + uniform.texture_binding = texture_binding; + if (uniform.array_size > 0) { + texture_binding += uniform.array_size; } else { ++texture_binding; } - uniform2.order = -1; + uniform.order = -1; if (_validate_datatype(type) != OK) { return ERR_PARSE_ERROR; } @@ -8182,20 +8269,20 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f _set_error(vformat(RTR("The '%s' qualifier is not supported for matrix types."), "SCOPE_INSTANCE")); return ERR_PARSE_ERROR; } - uniform2.texture_order = -1; + uniform.texture_order = -1; if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) { - uniform2.order = uniforms++; + uniform.order = uniforms++; #ifdef DEBUG_ENABLED if (check_device_limit_warnings) { - if (uniform2.array_size > 0) { - int size = get_datatype_size(uniform2.type) * uniform2.array_size; - int m = (16 * uniform2.array_size); + if (uniform.array_size > 0) { + int size = get_datatype_size(uniform.type) * uniform.array_size; + int m = (16 * uniform.array_size); if ((size % m) != 0U) { size += m - (size % m); } uniform_buffer_size += size; } else { - uniform_buffer_size += get_datatype_size(uniform2.type); + uniform_buffer_size += get_datatype_size(uniform.type); } if (uniform_buffer_exceeded_line == -1 && uniform_buffer_size > max_uniform_buffer_size) { @@ -8206,7 +8293,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f } } - if (uniform2.array_size > 0) { + if (uniform.array_size > 0) { if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL) { _set_error(vformat(RTR("The '%s' qualifier is not supported for uniform arrays."), "SCOPE_GLOBAL")); return ERR_PARSE_ERROR; @@ -8222,7 +8309,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f if (tk.type == TK_COLON) { completion_type = COMPLETION_HINT; completion_base = type; - completion_base_array = uniform2.array_size > 0; + completion_base_array = uniform.array_size > 0; //hint do { @@ -8234,180 +8321,251 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f return ERR_PARSE_ERROR; } - if (uniform2.array_size > 0) { + if (uniform.array_size > 0) { if (tk.type != TK_HINT_SOURCE_COLOR) { _set_error(RTR("This hint is not supported for uniform arrays.")); return ERR_PARSE_ERROR; } } - if (tk.type == TK_HINT_DEFAULT_WHITE_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_DEFAULT_WHITE; - } else if (tk.type == TK_HINT_DEFAULT_BLACK_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_DEFAULT_BLACK; - } else if (tk.type == TK_HINT_NORMAL_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_NORMAL; - } else if (tk.type == TK_HINT_ROUGHNESS_NORMAL_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL; - } else if (tk.type == TK_HINT_ROUGHNESS_R) { - uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_R; - } else if (tk.type == TK_HINT_ROUGHNESS_G) { - uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_G; - } else if (tk.type == TK_HINT_ROUGHNESS_B) { - uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_B; - } else if (tk.type == TK_HINT_ROUGHNESS_A) { - uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_A; - } else if (tk.type == TK_HINT_ROUGHNESS_GRAY) { - uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_GRAY; - } else if (tk.type == TK_HINT_ANISOTROPY_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_ANISOTROPY; - } else if (tk.type == TK_HINT_SOURCE_COLOR) { - if (type != TYPE_VEC3 && type != TYPE_VEC4 && type <= TYPE_MAT4) { - _set_error(vformat(RTR("Source color hint is for '%s', '%s' or sampler types only."), "vec3", "vec4")); - return ERR_PARSE_ERROR; - } - uniform2.hint = ShaderNode::Uniform::HINT_SOURCE_COLOR; - } else if (tk.type == TK_HINT_RANGE) { - uniform2.hint = ShaderNode::Uniform::HINT_RANGE; - if (type != TYPE_FLOAT && type != TYPE_INT) { - _set_error(vformat(RTR("Range hint is for '%s' and '%s' only."), "float", "int")); - return ERR_PARSE_ERROR; - } - - tk = _get_token(); - if (tk.type != TK_PARENTHESIS_OPEN) { - _set_expected_after_error("(", "hint_range"); - return ERR_PARSE_ERROR; - } + ShaderNode::Uniform::Hint new_hint = ShaderNode::Uniform::HINT_NONE; + TextureFilter new_filter = FILTER_DEFAULT; + TextureRepeat new_repeat = REPEAT_DEFAULT; - tk = _get_token(); + switch (tk.type) { + case TK_HINT_SOURCE_COLOR: { + if (type != TYPE_VEC3 && type != TYPE_VEC4 && !is_sampler_type(type)) { + _set_error(vformat(RTR("Source color hint is for '%s', '%s' or sampler types only."), "vec3", "vec4")); + return ERR_PARSE_ERROR; + } - float sign = 1.0; + if (is_sampler_type(type)) { + if (uniform.use_color) { + _set_error(vformat(RTR("Duplicated hint: '%s'."), "source_color")); + return ERR_PARSE_ERROR; + } + uniform.use_color = true; + } else { + new_hint = ShaderNode::Uniform::HINT_SOURCE_COLOR; + } + } break; + case TK_HINT_DEFAULT_BLACK_TEXTURE: { + new_hint = ShaderNode::Uniform::HINT_DEFAULT_BLACK; + } break; + case TK_HINT_DEFAULT_WHITE_TEXTURE: { + new_hint = ShaderNode::Uniform::HINT_DEFAULT_WHITE; + } break; + case TK_HINT_NORMAL_TEXTURE: { + new_hint = ShaderNode::Uniform::HINT_NORMAL; + } break; + case TK_HINT_ROUGHNESS_NORMAL_TEXTURE: { + new_hint = ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL; + } break; + case TK_HINT_ROUGHNESS_R: { + new_hint = ShaderNode::Uniform::HINT_ROUGHNESS_R; + } break; + case TK_HINT_ROUGHNESS_G: { + new_hint = ShaderNode::Uniform::HINT_ROUGHNESS_G; + } break; + case TK_HINT_ROUGHNESS_B: { + new_hint = ShaderNode::Uniform::HINT_ROUGHNESS_B; + } break; + case TK_HINT_ROUGHNESS_A: { + new_hint = ShaderNode::Uniform::HINT_ROUGHNESS_A; + } break; + case TK_HINT_ROUGHNESS_GRAY: { + new_hint = ShaderNode::Uniform::HINT_ROUGHNESS_GRAY; + } break; + case TK_HINT_ANISOTROPY_TEXTURE: { + new_hint = ShaderNode::Uniform::HINT_ANISOTROPY; + } break; + case TK_HINT_RANGE: { + if (type != TYPE_FLOAT && type != TYPE_INT) { + _set_error(vformat(RTR("Range hint is for '%s' and '%s' only."), "float", "int")); + return ERR_PARSE_ERROR; + } - if (tk.type == TK_OP_SUB) { - sign = -1.0; tk = _get_token(); - } - - if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { - _set_error(RTR("Expected an integer constant.")); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_expected_after_error("(", "hint_range"); + return ERR_PARSE_ERROR; + } - uniform2.hint_range[0] = tk.constant; - uniform2.hint_range[0] *= sign; + tk = _get_token(); - tk = _get_token(); + float sign = 1.0; - if (tk.type != TK_COMMA) { - _set_error(RTR("Expected ',' after integer constant.")); - return ERR_PARSE_ERROR; - } + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } - tk = _get_token(); + if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { + _set_error(RTR("Expected an integer constant.")); + return ERR_PARSE_ERROR; + } - sign = 1.0; + uniform.hint_range[0] = tk.constant; + uniform.hint_range[0] *= sign; - if (tk.type == TK_OP_SUB) { - sign = -1.0; tk = _get_token(); - } - if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { - _set_error(RTR("Expected an integer constant after ','.")); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_COMMA) { + _set_error(RTR("Expected ',' after integer constant.")); + return ERR_PARSE_ERROR; + } - uniform2.hint_range[1] = tk.constant; - uniform2.hint_range[1] *= sign; + tk = _get_token(); - tk = _get_token(); + sign = 1.0; - if (tk.type == TK_COMMA) { - tk = _get_token(); + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { _set_error(RTR("Expected an integer constant after ','.")); return ERR_PARSE_ERROR; } - uniform2.hint_range[2] = tk.constant; + uniform.hint_range[1] = tk.constant; + uniform.hint_range[1] *= sign; + tk = _get_token(); - } else { - if (type == TYPE_INT) { - uniform2.hint_range[2] = 1; + + if (tk.type == TK_COMMA) { + tk = _get_token(); + + if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { + _set_error(RTR("Expected an integer constant after ','.")); + return ERR_PARSE_ERROR; + } + + uniform.hint_range[2] = tk.constant; + tk = _get_token(); } else { - uniform2.hint_range[2] = 0.001; + if (type == TYPE_INT) { + uniform.hint_range[2] = 1; + } else { + uniform.hint_range[2] = 0.001; + } } - } - if (tk.type != TK_PARENTHESIS_CLOSE) { - _set_expected_error(")"); - return ERR_PARSE_ERROR; - } - } else if (tk.type == TK_HINT_INSTANCE_INDEX) { - if (custom_instance_index != -1) { - _set_error(vformat(RTR("Can only specify '%s' once."), "instance_index")); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_expected_error(")"); + return ERR_PARSE_ERROR; + } - tk = _get_token(); - if (tk.type != TK_PARENTHESIS_OPEN) { - _set_expected_after_error("(", "instance_index"); - return ERR_PARSE_ERROR; - } + new_hint = ShaderNode::Uniform::HINT_RANGE; + } break; + case TK_HINT_INSTANCE_INDEX: { + if (custom_instance_index != -1) { + _set_error(vformat(RTR("Can only specify '%s' once."), "instance_index")); + return ERR_PARSE_ERROR; + } - tk = _get_token(); + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_expected_after_error("(", "instance_index"); + return ERR_PARSE_ERROR; + } - if (tk.type == TK_OP_SUB) { - _set_error(RTR("The instance index can't be negative.")); - return ERR_PARSE_ERROR; - } + tk = _get_token(); - if (!tk.is_integer_constant()) { - _set_error(RTR("Expected an integer constant.")); - return ERR_PARSE_ERROR; - } + if (tk.type == TK_OP_SUB) { + _set_error(RTR("The instance index can't be negative.")); + return ERR_PARSE_ERROR; + } + + if (!tk.is_integer_constant()) { + _set_error(RTR("Expected an integer constant.")); + return ERR_PARSE_ERROR; + } + + custom_instance_index = tk.constant; - custom_instance_index = tk.constant; + if (custom_instance_index >= MAX_INSTANCE_UNIFORM_INDICES) { + _set_error(vformat(RTR("Allowed instance uniform indices must be within [0..%d] range."), MAX_INSTANCE_UNIFORM_INDICES - 1)); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_expected_error(")"); + return ERR_PARSE_ERROR; + } + } break; + case TK_FILTER_NEAREST: { + new_filter = FILTER_NEAREST; + } break; + case TK_FILTER_LINEAR: { + new_filter = FILTER_LINEAR; + } break; + case TK_FILTER_NEAREST_MIPMAP: { + new_filter = FILTER_NEAREST_MIPMAP; + } break; + case TK_FILTER_LINEAR_MIPMAP: { + new_filter = FILTER_LINEAR_MIPMAP; + } break; + case TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC: { + new_filter = FILTER_NEAREST_MIPMAP_ANISOTROPIC; + } break; + case TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC: { + new_filter = FILTER_LINEAR_MIPMAP_ANISOTROPIC; + } break; + case TK_REPEAT_DISABLE: { + new_repeat = REPEAT_DISABLE; + } break; + case TK_REPEAT_ENABLE: { + new_repeat = REPEAT_ENABLE; + } break; + default: + break; + } + if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || (new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE)) && !is_sampler_type(type)) { + _set_error(RTR("This hint is only for sampler types.")); + return ERR_PARSE_ERROR; + } - if (custom_instance_index >= MAX_INSTANCE_UNIFORM_INDICES) { - _set_error(vformat(RTR("Allowed instance uniform indices must be within [0..%d] range."), MAX_INSTANCE_UNIFORM_INDICES - 1)); + if (new_hint != ShaderNode::Uniform::HINT_NONE) { + if (uniform.hint != ShaderNode::Uniform::HINT_NONE) { + if (uniform.hint == new_hint) { + _set_error(vformat(RTR("Duplicated hint: '%s'."), get_uniform_hint_name(new_hint))); + } else { + _set_error(vformat(RTR("Redefinition of hint: '%s'. The hint has already been set to '%s'."), get_uniform_hint_name(new_hint), get_uniform_hint_name(uniform.hint))); + } return ERR_PARSE_ERROR; + } else { + uniform.hint = new_hint; } + } - tk = _get_token(); - - if (tk.type != TK_PARENTHESIS_CLOSE) { - _set_expected_error(")"); + if (new_filter != FILTER_DEFAULT) { + if (uniform.filter != FILTER_DEFAULT) { + if (uniform.filter == new_filter) { + _set_error(vformat(RTR("Duplicated hint: '%s'."), get_texture_filter_name(new_filter))); + } else { + _set_error(vformat(RTR("Redefinition of hint: '%s'. The filter mode has already been set to '%s'."), get_texture_filter_name(new_filter), get_texture_filter_name(uniform.filter))); + } return ERR_PARSE_ERROR; + } else { + uniform.filter = new_filter; } - } else if (tk.type == TK_FILTER_LINEAR) { - uniform2.filter = FILTER_LINEAR; - } else if (tk.type == TK_FILTER_NEAREST) { - uniform2.filter = FILTER_NEAREST; - } else if (tk.type == TK_FILTER_NEAREST_MIPMAP) { - uniform2.filter = FILTER_NEAREST_MIPMAP; - } else if (tk.type == TK_FILTER_LINEAR_MIPMAP) { - uniform2.filter = FILTER_LINEAR_MIPMAP; - } else if (tk.type == TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC) { - uniform2.filter = FILTER_NEAREST_MIPMAP_ANISOTROPIC; - } else if (tk.type == TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC) { - uniform2.filter = FILTER_LINEAR_MIPMAP_ANISOTROPIC; - } else if (tk.type == TK_REPEAT_DISABLE) { - uniform2.repeat = REPEAT_DISABLE; - } else if (tk.type == TK_REPEAT_ENABLE) { - uniform2.repeat = REPEAT_ENABLE; } - if (uniform2.hint == ShaderNode::Uniform::HINT_SOURCE_COLOR) { - if (type != TYPE_VEC3 && type != TYPE_VEC4 && !is_sampler_type(type)) { - _set_error(vformat(RTR("This hint is only for '%s', '%s' or sampler types."), "vec3", "vec4")); + if (new_repeat != REPEAT_DEFAULT) { + if (uniform.repeat != REPEAT_DEFAULT) { + if (uniform.repeat == new_repeat) { + _set_error(vformat(RTR("Duplicated hint: '%s'."), get_texture_repeat_name(new_repeat))); + } else { + _set_error(vformat(RTR("Redefinition of hint: '%s'. The repeat mode has already been set to '%s'."), get_texture_repeat_name(new_repeat), get_texture_repeat_name(uniform.repeat))); + } return ERR_PARSE_ERROR; + } else { + uniform.repeat = new_repeat; } - } else if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && !is_sampler_type(type)) { - _set_error(RTR("This hint is only for sampler types.")); - return ERR_PARSE_ERROR; } tk = _get_token(); @@ -8417,9 +8575,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) { if (custom_instance_index >= 0) { - uniform2.instance_index = custom_instance_index; + uniform.instance_index = custom_instance_index; } else { - uniform2.instance_index = instance_index++; + uniform.instance_index = instance_index++; if (instance_index > MAX_INSTANCE_UNIFORM_INDICES) { _set_error(vformat(RTR("Too many '%s' uniforms in shader, maximum supported is %d."), "instance", MAX_INSTANCE_UNIFORM_INDICES)); return ERR_PARSE_ERROR; @@ -8430,7 +8588,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f //reset scope for next uniform if (tk.type == TK_OP_ASSIGN) { - if (uniform2.array_size > 0) { + if (uniform.array_size > 0) { _set_error(RTR("Setting default values to uniform arrays is not supported.")); return ERR_PARSE_ERROR; } @@ -8446,16 +8604,16 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f ConstantNode *cn = static_cast<ConstantNode *>(expr); - uniform2.default_value.resize(cn->values.size()); + uniform.default_value.resize(cn->values.size()); - if (!convert_constant(cn, uniform2.type, uniform2.default_value.ptrw())) { - _set_error(vformat(RTR("Can't convert constant to '%s'."), get_datatype_name(uniform2.type))); + if (!convert_constant(cn, uniform.type, uniform.default_value.ptrw())) { + _set_error(vformat(RTR("Can't convert constant to '%s'."), get_datatype_name(uniform.type))); return ERR_PARSE_ERROR; } tk = _get_token(); } - shader->uniforms[name] = uniform2; + shader->uniforms[name] = uniform; #ifdef DEBUG_ENABLED if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG)) { used_uniforms.insert(name, Usage(tk_line)); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 5d216bd66e..a4226a9764 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -681,6 +681,7 @@ public: Vector<ConstantNode::Value> default_value; Scope scope = SCOPE_LOCAL; Hint hint = HINT_NONE; + bool use_color = false; TextureFilter filter = FILTER_DEFAULT; TextureRepeat repeat = REPEAT_DEFAULT; float hint_range[3]; @@ -756,6 +757,9 @@ public: static DataPrecision get_token_precision(TokenType p_type); static String get_precision_name(DataPrecision p_type); static String get_datatype_name(DataType p_type); + static String get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint); + static String get_texture_filter_name(TextureFilter p_filter); + static String get_texture_repeat_name(TextureRepeat p_repeat); static bool is_token_nonvoid_datatype(TokenType p_type); static bool is_token_operator(TokenType p_type); static bool is_token_operator_assign(TokenType p_type); |