diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a0f8b59117..8fcd5bacb6 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -66,6 +66,11 @@ Size2 EditorProperty::get_minimum_size() const { ms.width += key->get_width() + get_theme_constant("hseparator", "Tree"); } + if (deletable) { + Ref key = get_theme_icon("Close", "EditorIcons"); + ms.width += key->get_width() + get_theme_constant("hseparator", "Tree"); + } + if (checkable) { Ref check = get_theme_icon("checked", "CheckBox"); ms.width += check->get_width() + get_theme_constant("hseparation", "CheckBox") + get_theme_constant("hseparator", "Tree"); @@ -154,6 +159,18 @@ void EditorProperty::_notification(int p_what) { text_size -= key->get_width() + 4 * EDSCALE; } } + + if (deletable) { + Ref close; + + close = get_theme_icon("Close", "EditorIcons"); + + rect.size.x -= close->get_width() + get_theme_constant("hseparator", "Tree"); + + if (no_children) { + text_size -= close->get_width() + 4 * EDSCALE; + } + } } //set children @@ -278,6 +295,25 @@ void EditorProperty::_notification(int p_what) { } else { keying_rect = Rect2(); } + + if (deletable) { + Ref close; + + close = get_theme_icon("Close", "EditorIcons"); + + ofs = size.width - close->get_width() - get_theme_constant("hseparator", "Tree"); + + Color color2(1, 1, 1); + if (delete_hover) { + color2.r *= 1.2; + color2.g *= 1.2; + color2.b *= 1.2; + } + delete_rect = Rect2(ofs, ((size.height - close->get_height()) / 2), close->get_width(), close->get_height()); + draw_texture(close, delete_rect.position, color2); + } else { + delete_rect = Rect2(); + } } } @@ -547,6 +583,16 @@ void EditorProperty::set_keying(bool p_keying) { queue_sort(); } +void EditorProperty::set_deletable(bool p_deletable) { + deletable = p_deletable; + update(); + queue_sort(); +} + +bool EditorProperty::is_deletable() const { + return deletable; +} + bool EditorProperty::is_keying() const { return keying; } @@ -619,6 +665,12 @@ void EditorProperty::_gui_input(const Ref &p_event) { update(); } + bool new_delete_hover = delete_rect.has_point(me->get_position()) && !button_left; + if (new_delete_hover != delete_hover) { + delete_hover = new_delete_hover; + update(); + } + bool new_revert_hover = revert_rect.has_point(me->get_position()) && !button_left; if (new_revert_hover != revert_hover) { revert_hover = new_revert_hover; @@ -662,6 +714,9 @@ void EditorProperty::_gui_input(const Ref &p_event) { call_deferred("update_property"); } } + if (delete_rect.has_point(mb->get_position())) { + emit_signal("property_deleted", property); + } if (revert_rect.has_point(mb->get_position())) { @@ -821,6 +876,9 @@ void EditorProperty::_bind_methods() { ClassDB::bind_method(D_METHOD("set_keying", "keying"), &EditorProperty::set_keying); ClassDB::bind_method(D_METHOD("is_keying"), &EditorProperty::is_keying); + ClassDB::bind_method(D_METHOD("set_deletable", "deletable"), &EditorProperty::set_deletable); + ClassDB::bind_method(D_METHOD("is_deletable"), &EditorProperty::is_deletable); + ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property); ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object); @@ -839,9 +897,11 @@ void EditorProperty::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deletable"), "set_deletable", "is_deletable"); ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::PACKED_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value"))); ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING_NAME, "property"))); + ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING_NAME, "property"))); ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::STRING, "bool"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); @@ -865,6 +925,7 @@ EditorProperty::EditorProperty() { checked = false; draw_red = false; keying = false; + deletable = false; keying_hover = false; revert_hover = false; check_hover = false; @@ -926,7 +987,7 @@ void EditorInspectorPlugin::parse_category(Object *p_object, const String &p_par } } -bool EditorInspectorPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { +bool EditorInspectorPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { if (get_script_instance()) { Variant arg[6] = { @@ -1276,11 +1337,11 @@ EditorInspectorSection::~EditorInspectorSection() { Ref EditorInspector::inspector_plugins[MAX_PLUGINS]; int EditorInspector::inspector_plugin_count = 0; -EditorProperty *EditorInspector::instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { +EditorProperty *EditorInspector::instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { for (int i = inspector_plugin_count - 1; i >= 0; i--) { - inspector_plugins[i]->parse_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage); + inspector_plugins[i]->parse_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide); if (inspector_plugins[i]->added_editors.size()) { for (int j = 1; j < inspector_plugins[i]->added_editors.size(); j++) { //only keep first one memdelete(inspector_plugins[i]->added_editors[j].property_editor); @@ -1362,6 +1423,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Refobject = object; ep->connect("property_changed", callable_mp(this, &EditorInspector::_property_changed)); ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); + ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); @@ -1394,6 +1456,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Refset_read_only(read_only); ep->update_property(); ep->update_reload_status(); + ep->set_deletable(deletable_properties); } } ped->added_editors.clear(); @@ -1762,7 +1825,7 @@ void EditorInspector::update_tree() { for (List>::Element *E = valid_plugins.front(); E; E = E->next()) { Ref ped = E->get(); - bool exclusive = ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage); + bool exclusive = ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage, wide_editors); List editors = ped->added_editors; //make a copy, since plugins may be used again in a sub-inspector ped->added_editors.clear(); @@ -1806,6 +1869,7 @@ void EditorInspector::update_tree() { ep->set_keying(keying); ep->set_read_only(read_only); + ep->set_deletable(deletable_properties); } current_vbox->add_child(F->get().property_editor); @@ -1817,6 +1881,7 @@ void EditorInspector::update_tree() { ep->connect("property_changed", callable_mp(this, &EditorInspector::_property_changed_update_all), varray(), CONNECT_DEFERRED); } ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); + ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); @@ -2000,6 +2065,10 @@ int EditorInspector::get_scroll_offset() const { return get_v_scroll(); } +void EditorInspector::set_use_wide_editors(bool p_enable) { + wide_editors = p_enable; +} + void EditorInspector::set_sub_inspector(bool p_enable) { sub_inspector = p_enable; @@ -2013,6 +2082,10 @@ void EditorInspector::set_sub_inspector(bool p_enable) { } } +void EditorInspector::set_use_deletable_properties(bool p_enabled) { + deletable_properties = p_enabled; +} + void EditorInspector::_edit_request_change(Object *p_object, const String &p_property) { if (object != p_object) //may be undoing/redoing for a non edited object, so ignore @@ -2145,6 +2218,15 @@ void EditorInspector::_property_keyed(const String &p_path, bool p_advance) { emit_signal("property_keyed", p_path, object->get(p_path), p_advance); //second param is deprecated } +void EditorInspector::_property_deleted(const String &p_path) { + + print_line("deleted pressed?"); + if (!object) + return; + + emit_signal("property_deleted", p_path); //second param is deprecated +} + void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance) { if (!object) @@ -2348,6 +2430,7 @@ void EditorInspector::_bind_methods() { ADD_SIGNAL(MethodInfo("property_selected", PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop"))); ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property"))); @@ -2365,6 +2448,7 @@ EditorInspector::EditorInspector() { set_enable_h_scroll(false); set_enable_v_scroll(true); + wide_editors = false; show_categories = false; hide_script = true; use_doc_hints = false; @@ -2383,6 +2467,7 @@ EditorInspector::EditorInspector() { set_process(true); property_focusable = -1; sub_inspector = false; + deletable_properties = false; get_v_scrollbar()->connect("value_changed", callable_mp(this, &EditorInspector::_vscroll_changed)); update_scroll_request = -1; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index b49a4424f6..c8c1ecc49a 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -64,6 +64,7 @@ private: bool checked; bool draw_red; bool keying; + bool deletable; Rect2 right_child_rect; Rect2 bottom_child_rect; @@ -74,6 +75,8 @@ private: bool revert_hover; Rect2 check_rect; bool check_hover; + Rect2 delete_rect; + bool delete_hover; bool can_revert; @@ -133,6 +136,8 @@ public: void set_keying(bool p_keying); bool is_keying() const; + void set_deletable(bool p_enable); + bool is_deletable() const; void add_focusable(Control *p_control); void select(int p_focusable = -1); void deselect(); @@ -190,7 +195,7 @@ public: virtual bool can_handle(Object *p_object); virtual void parse_begin(Object *p_object); virtual void parse_category(Object *p_object, const String &p_parse_category); - virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); virtual void parse_end(); }; @@ -283,6 +288,8 @@ class EditorInspector : public ScrollContainer { bool read_only; bool keying; bool sub_inspector; + bool wide_editors; + bool deletable_properties; float refresh_countdown; bool update_tree_pending; @@ -307,6 +314,7 @@ class EditorInspector : public ScrollContainer { void _multiple_properties_changed(Vector p_paths, Array p_values); void _property_keyed(const String &p_path, bool p_advance); void _property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance); + void _property_deleted(const String &p_path); void _property_checked(const String &p_path, bool p_checked); @@ -337,7 +345,7 @@ public: static void remove_inspector_plugin(const Ref &p_plugin); static void cleanup_plugins(); - static EditorProperty *instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + static EditorProperty *instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); void set_undo_redo(UndoRedo *p_undo_redo); @@ -380,8 +388,11 @@ public: void set_object_class(const String &p_class); String get_object_class() const; + void set_use_wide_editors(bool p_enable); void set_sub_inspector(bool p_enable); + void set_use_deletable_properties(bool p_enabled); + EditorInspector(); }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 6f6287ccb5..fddc5dc231 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -708,6 +708,11 @@ void EditorNode::_sources_changed(bool p_exist) { if (waiting_for_first_scan) { waiting_for_first_scan = false; + // Reload the global shader variables, but this time + // loading texures, as they are now properly imported. + print_line("done scanning, reload textures"); + RenderingServer::get_singleton()->global_variables_load_settings(true); + // Start preview thread now that it's safe. if (!singleton->cmdline_export_mode) { EditorResourcePreview::get_singleton()->start(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index cf478f20e5..5213d7ec15 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1150,12 +1150,15 @@ void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, boo } } -EditorPropertyVector2::EditorPropertyVector2() { +EditorPropertyVector2::EditorPropertyVector2(bool p_force_wide) { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector2_editing"); BoxContainer *bc; - if (horizontal) { + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { bc = memnew(HBoxContainer); add_child(bc); set_bottom_editor(bc); @@ -1231,13 +1234,16 @@ void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool } } -EditorPropertyRect2::EditorPropertyRect2() { +EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + bool horizontal = !p_force_wide && bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); BoxContainer *bc; - if (horizontal) { + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { bc = memnew(HBoxContainer); add_child(bc); set_bottom_editor(bc); @@ -1311,12 +1317,15 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo } } -EditorPropertyVector3::EditorPropertyVector3() { +EditorPropertyVector3::EditorPropertyVector3(bool p_force_wide) { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; - if (horizontal) { + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { bc = memnew(HBoxContainer); add_child(bc); set_bottom_editor(bc); @@ -1343,6 +1352,255 @@ EditorPropertyVector3::EditorPropertyVector3() { } setting = false; } + +///////////////////// VECTOR2i ///////////////////////// + +void EditorPropertyVector2i::_value_changed(double val, const String &p_name) { + if (setting) + return; + + Vector2i v2; + v2.x = spin[0]->get_value(); + v2.y = spin[1]->get_value(); + emit_changed(get_edited_property(), v2, p_name); +} + +void EditorPropertyVector2i::update_property() { + Vector2i val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.x); + spin[1]->set_value(val.y); + setting = false; +} + +void EditorPropertyVector2i::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + Color base = get_theme_color("accent_color", "Editor"); + for (int i = 0; i < 2; i++) { + + Color c = base; + c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); + spin[i]->set_custom_label_color(true, c); + } + } +} + +void EditorPropertyVector2i::_bind_methods() { +} + +void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_no_slider) { + for (int i = 0; i < 2; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(1); + spin[i]->set_hide_slider(p_no_slider); + spin[i]->set_allow_greater(true); + spin[i]->set_allow_lesser(true); + } +} + +EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) { + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector2_editing"); + + BoxContainer *bc; + + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + + static const char *desc[2] = { "x", "y" }; + for (int i = 0; i < 2; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_flat(true); + spin[i]->set_label(desc[i]); + bc->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector2i::_value_changed), varray(desc[i])); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this + } + setting = false; +} + +///////////////////// RECT2 ///////////////////////// + +void EditorPropertyRect2i::_value_changed(double val, const String &p_name) { + if (setting) + return; + + Rect2i r2; + r2.position.x = spin[0]->get_value(); + r2.position.y = spin[1]->get_value(); + r2.size.x = spin[2]->get_value(); + r2.size.y = spin[3]->get_value(); + emit_changed(get_edited_property(), r2, p_name); +} + +void EditorPropertyRect2i::update_property() { + Rect2i val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.position.x); + spin[1]->set_value(val.position.y); + spin[2]->set_value(val.size.x); + spin[3]->set_value(val.size.y); + setting = false; +} +void EditorPropertyRect2i::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + Color base = get_theme_color("accent_color", "Editor"); + for (int i = 0; i < 4; i++) { + + Color c = base; + c.set_hsv(float(i % 2) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); + spin[i]->set_custom_label_color(true, c); + } + } +} +void EditorPropertyRect2i::_bind_methods() { +} + +void EditorPropertyRect2i::setup(int p_min, int p_max, bool p_no_slider) { + for (int i = 0; i < 4; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(1); + spin[i]->set_hide_slider(p_no_slider); + spin[i]->set_allow_greater(true); + spin[i]->set_allow_lesser(true); + } +} + +EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) { + + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + + BoxContainer *bc; + + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + + static const char *desc[4] = { "x", "y", "w", "h" }; + for (int i = 0; i < 4; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + spin[i]->set_flat(true); + bc->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyRect2i::_value_changed), varray(desc[i])); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this + } + setting = false; +} + +///////////////////// VECTOR3 ///////////////////////// + +void EditorPropertyVector3i::_value_changed(double val, const String &p_name) { + if (setting) + return; + + Vector3i v3; + v3.x = spin[0]->get_value(); + v3.y = spin[1]->get_value(); + v3.z = spin[2]->get_value(); + emit_changed(get_edited_property(), v3, p_name); +} + +void EditorPropertyVector3i::update_property() { + Vector3i val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.x); + spin[1]->set_value(val.y); + spin[2]->set_value(val.z); + setting = false; +} +void EditorPropertyVector3i::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + Color base = get_theme_color("accent_color", "Editor"); + for (int i = 0; i < 3; i++) { + + Color c = base; + c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); + spin[i]->set_custom_label_color(true, c); + } + } +} +void EditorPropertyVector3i::_bind_methods() { +} + +void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_no_slider) { + for (int i = 0; i < 3; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(1); + spin[i]->set_hide_slider(p_no_slider); + spin[i]->set_allow_greater(true); + spin[i]->set_allow_lesser(true); + } +} + +EditorPropertyVector3i::EditorPropertyVector3i(bool p_force_wide) { + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + + BoxContainer *bc; + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + + static const char *desc[3] = { "x", "y", "z" }; + for (int i = 0; i < 3; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_flat(true); + spin[i]->set_label(desc[i]); + bc->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector3i::_value_changed), varray(desc[i])); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this + } + setting = false; +} + ///////////////////// PLANE ///////////////////////// void EditorPropertyPlane::_value_changed(double val, const String &p_name) { @@ -1391,13 +1649,16 @@ void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool } } -EditorPropertyPlane::EditorPropertyPlane() { +EditorPropertyPlane::EditorPropertyPlane(bool p_force_wide) { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; - if (horizontal) { + if (p_force_wide) { + bc = memnew(HBoxContainer); + add_child(bc); + } else if (horizontal) { bc = memnew(HBoxContainer); add_child(bc); set_bottom_editor(bc); @@ -2877,7 +3138,7 @@ void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) { //do none } -bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { +bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { float default_float_step = EDITOR_GET("interface/inspector/default_float_step"); @@ -3083,7 +3344,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ // math types case Variant::VECTOR2: { - EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2(p_wide)); double min = -65535, max = 65535, step = default_float_step; bool hide_slider = true; @@ -3099,9 +3360,24 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, hide_slider); add_property_editor(p_path, editor); + } break; + case Variant::VECTOR2I: { + EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide)); + int min = -65535, max = 65535; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + hide_slider = false; + } + + editor->setup(min, max, hide_slider); + add_property_editor(p_path, editor); + } break; case Variant::RECT2: { - EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2(p_wide)); double min = -65535, max = 65535, step = default_float_step; bool hide_slider = true; @@ -3117,8 +3393,22 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, hide_slider); add_property_editor(p_path, editor); } break; + case Variant::RECT2I: { + EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide)); + int min = -65535, max = 65535; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + hide_slider = false; + } + + editor->setup(min, max, hide_slider); + add_property_editor(p_path, editor); + } break; case Variant::VECTOR3: { - EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide)); double min = -65535, max = 65535, step = default_float_step; bool hide_slider = true; @@ -3134,6 +3424,22 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, hide_slider); add_property_editor(p_path, editor); + } break; + case Variant::VECTOR3I: { + EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide)); + int min = -65535, max = 65535; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + + hide_slider = false; + } + + editor->setup(min, max, hide_slider); + add_property_editor(p_path, editor); + } break; case Variant::TRANSFORM2D: { EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); @@ -3154,7 +3460,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } break; case Variant::PLANE: { - EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + EditorPropertyPlane *editor = memnew(EditorPropertyPlane(p_wide)); double min = -65535, max = 65535, step = default_float_step; bool hide_slider = true; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index c5fc8aaf77..61c11f4534 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -361,7 +361,7 @@ protected: public: virtual void update_property(); void setup(double p_min, double p_max, double p_step, bool p_no_slider); - EditorPropertyVector2(); + EditorPropertyVector2(bool p_force_wide = false); }; class EditorPropertyRect2 : public EditorProperty { @@ -377,7 +377,7 @@ protected: public: virtual void update_property(); void setup(double p_min, double p_max, double p_step, bool p_no_slider); - EditorPropertyRect2(); + EditorPropertyRect2(bool p_force_wide = false); }; class EditorPropertyVector3 : public EditorProperty { @@ -393,7 +393,55 @@ protected: public: virtual void update_property(); void setup(double p_min, double p_max, double p_step, bool p_no_slider); - EditorPropertyVector3(); + EditorPropertyVector3(bool p_force_wide = false); +}; + +class EditorPropertyVector2i : public EditorProperty { + GDCLASS(EditorPropertyVector2i, EditorProperty); + EditorSpinSlider *spin[2]; + bool setting; + void _value_changed(double p_val, const String &p_name); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(int p_min, int p_max, bool p_no_slider); + EditorPropertyVector2i(bool p_force_wide = false); +}; + +class EditorPropertyRect2i : public EditorProperty { + GDCLASS(EditorPropertyRect2i, EditorProperty); + EditorSpinSlider *spin[4]; + bool setting; + void _value_changed(double p_val, const String &p_name); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(int p_min, int p_max, bool p_no_slider); + EditorPropertyRect2i(bool p_force_wide = false); +}; + +class EditorPropertyVector3i : public EditorProperty { + GDCLASS(EditorPropertyVector3i, EditorProperty); + EditorSpinSlider *spin[3]; + bool setting; + void _value_changed(double p_val, const String &p_name); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(int p_min, int p_max, bool p_no_slider); + EditorPropertyVector3i(bool p_force_wide = false); }; class EditorPropertyPlane : public EditorProperty { @@ -409,7 +457,7 @@ protected: public: virtual void update_property(); void setup(double p_min, double p_max, double p_step, bool p_no_slider); - EditorPropertyPlane(); + EditorPropertyPlane(bool p_force_wide = false); }; class EditorPropertyQuat : public EditorProperty { @@ -626,7 +674,7 @@ class EditorInspectorDefaultPlugin : public EditorInspectorPlugin { public: virtual bool can_handle(Object *p_object); virtual void parse_begin(Object *p_object); - virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); virtual void parse_end(); }; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index b4ce60171b..fdd5bd8db6 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -741,6 +741,13 @@ void EditorPropertyDictionary::update_property() { editor->setup(-100000, 100000, 0.001, true); prop = editor; + } break; + case Variant::VECTOR2I: { + + EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i); + editor->setup(-100000, 100000, true); + prop = editor; + } break; case Variant::RECT2: { @@ -748,6 +755,13 @@ void EditorPropertyDictionary::update_property() { editor->setup(-100000, 100000, 0.001, true); prop = editor; + } break; + case Variant::RECT2I: { + + EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i); + editor->setup(-100000, 100000, true); + prop = editor; + } break; case Variant::VECTOR3: { @@ -755,6 +769,13 @@ void EditorPropertyDictionary::update_property() { editor->setup(-100000, 100000, 0.001, true); prop = editor; + } break; + case Variant::VECTOR3I: { + + EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i); + editor->setup(-100000, 100000, true); + prop = editor; + } break; case Variant::TRANSFORM2D: { diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index 67e836082d..a7120c5d68 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -288,7 +288,7 @@ void EditorInspectorRootMotionPlugin::parse_begin(Object *p_object) { //do none } -bool EditorInspectorRootMotionPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { +bool EditorInspectorRootMotionPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { if (p_path == "root_motion_track" && p_object->is_class("AnimationTree") && p_type == Variant::NODE_PATH) { EditorPropertyRootMotion *editor = memnew(EditorPropertyRootMotion); diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h index 8a7691de5d..f72ad1ec05 100644 --- a/editor/plugins/root_motion_editor_plugin.h +++ b/editor/plugins/root_motion_editor_plugin.h @@ -65,7 +65,7 @@ class EditorInspectorRootMotionPlugin : public EditorInspectorPlugin { public: virtual bool can_handle(Object *p_object); virtual void parse_begin(Object *p_object); - virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); virtual void parse_end(); }; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 2a36700105..9ef8148241 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -193,6 +193,12 @@ void ShaderTextEditor::_check_shader_mode() { } } +static ShaderLanguage::DataType _get_global_variable_type(const StringName &p_variable) { + + RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(p_variable); + return RS::global_variable_type_get_shader_datatype(gvt); +} + void ShaderTextEditor::_code_complete_script(const String &p_code, List *r_options) { _check_shader_mode(); @@ -200,7 +206,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, Listget_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), r_options, calltip); + sl.complete(p_code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type, r_options, calltip); get_text_edit()->set_code_hint(calltip); } @@ -215,7 +221,7 @@ void ShaderTextEditor::_validate_script() { ShaderLanguage sl; - Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types()); + Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type); if (err != OK) { String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text(); diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index fbb6616dea..eb6e261305 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -45,7 +45,7 @@ void EditorInspectorPluginStyleBox::parse_begin(Object *p_object) { preview->edit(sb); add_custom_control(preview); } -bool EditorInspectorPluginStyleBox::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { +bool EditorInspectorPluginStyleBox::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { return false; //do not want } void EditorInspectorPluginStyleBox::parse_end() { diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h index f4a72d9d1c..1eea9260b2 100644 --- a/editor/plugins/style_box_editor_plugin.h +++ b/editor/plugins/style_box_editor_plugin.h @@ -62,7 +62,7 @@ class EditorInspectorPluginStyleBox : public EditorInspectorPlugin { public: virtual bool can_handle(Object *p_object); virtual void parse_begin(Object *p_object); - virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); virtual void parse_end(); }; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 294ce2c4cd..d5128db0d5 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -2261,6 +2261,12 @@ void VisualShaderEditor::_show_preview_text() { } } +static ShaderLanguage::DataType _get_global_variable_type(const StringName &p_variable) { + + RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(p_variable); + return RS::global_variable_type_get_shader_datatype(gvt); +} + void VisualShaderEditor::_update_preview() { if (!preview_showed) { @@ -2274,7 +2280,7 @@ void VisualShaderEditor::_update_preview() { ShaderLanguage sl; - Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_types()); + Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type); for (int i = 0; i < preview_text->get_line_count(); i++) { preview_text->set_line_as_marked(i, false); @@ -3291,7 +3297,7 @@ void EditorInspectorShaderModePlugin::parse_begin(Object *p_object) { //do none } -bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { +bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { if (p_path == "mode" && p_object->is_class("VisualShader") && p_type == Variant::INT) { diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 473c1bb070..a495b09b5c 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -338,7 +338,7 @@ class EditorInspectorShaderModePlugin : public EditorInspectorPlugin { public: virtual bool can_handle(Object *p_object); virtual void parse_begin(Object *p_object); - virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); virtual void parse_end(); }; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 42493191a8..49c02dc895 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -2135,6 +2135,11 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { tab_container->add_child(autoload_settings); autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::_settings_changed)); + shaders_global_variables_editor = memnew(ShaderGlobalsEditor); + shaders_global_variables_editor->set_name(TTR("Shader Globals")); + tab_container->add_child(shaders_global_variables_editor); + shaders_global_variables_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::_settings_changed)); + plugin_settings = memnew(EditorPluginSettings); plugin_settings->set_name(TTR("Plugins")); tab_container->add_child(plugin_settings); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 2cecb13198..5475bb5508 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -36,6 +36,7 @@ #include "editor/editor_data.h" #include "editor/editor_plugin_settings.h" #include "editor/editor_sectioned_inspector.h" +#include "editor/shader_globals_editor.h" #include "scene/gui/dialogs.h" #include "scene/gui/tab_container.h" @@ -85,6 +86,7 @@ class ProjectSettingsEditor : public AcceptDialog { OptionButton *device_index; Label *device_index_label; MenuButton *popup_copy_to_feature; + ShaderGlobalsEditor *shaders_global_variables_editor; LineEdit *action_name; Button *action_add; diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp new file mode 100644 index 0000000000..566ac54612 --- /dev/null +++ b/editor/shader_globals_editor.cpp @@ -0,0 +1,452 @@ +#include "shader_globals_editor.h" +#include "editor_node.h" + +static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { + "bool", + "bvec2", + "bvec3", + "bvec4", + "int", + "ivec2", + "ivec3", + "ivec4", + "rect2i", + "uint", + "uvec2", + "uvec3", + "uvec4", + "float", + "vec2", + "vec3", + "vec4", + "color", + "rect2", + "mat2", + "mat3", + "mat4", + "transform_2d", + "transform", + "sampler2D", + "sampler2DArray", + "sampler3D", + "samplerCube", +}; + +class ShaderGlobalsEditorInterface : public Object { + GDCLASS(ShaderGlobalsEditorInterface, Object) + + void _var_changed() { + emit_signal("var_changed"); + } + +protected: + static void _bind_methods() { + ClassDB::bind_method("_var_changed", &ShaderGlobalsEditorInterface::_var_changed); + ADD_SIGNAL(MethodInfo("var_changed")); + } + + bool _set(const StringName &p_name, const Variant &p_value) { + Variant existing = RS::get_singleton()->global_variable_get(p_name); + + if (existing.get_type() == Variant::NIL) { + return false; + } + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action("Set Shader Global Variable"); + undo_redo->add_do_method(RS::get_singleton(), "global_variable_set", p_name, p_value); + undo_redo->add_undo_method(RS::get_singleton(), "global_variable_set", p_name, existing); + RS::GlobalVariableType type = RS::get_singleton()->global_variable_get_type(p_name); + Dictionary gv; + gv["type"] = global_var_type_names[type]; + if (type >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { + RES res = p_value; + if (res.is_valid()) { + gv["value"] = res->get_path(); + } else { + gv["value"] = ""; + } + } else { + gv["value"] = p_value; + } + + String path = "shader_globals/" + String(p_name); + undo_redo->add_do_property(ProjectSettings::get_singleton(), path, gv); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), path, ProjectSettings::get_singleton()->get(path)); + undo_redo->add_do_method(this, "_var_changed"); + undo_redo->add_undo_method(this, "_var_changed"); + block_update = true; + undo_redo->commit_action(); + block_update = false; + + print_line("all good?"); + return true; + } + + bool _get(const StringName &p_name, Variant &r_ret) const { + r_ret = RS::get_singleton()->global_variable_get(p_name); + return r_ret.get_type() != Variant::NIL; + } + void _get_property_list(List *p_list) const { + Vector variables; + variables = RS::get_singleton()->global_variable_get_list(); + for (int i = 0; i < variables.size(); i++) { + PropertyInfo pinfo; + pinfo.name = variables[i]; + + switch (RS::get_singleton()->global_variable_get_type(variables[i])) { + case RS::GLOBAL_VAR_TYPE_BOOL: { + pinfo.type = Variant::BOOL; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC2: { + pinfo.type = Variant::INT; + pinfo.hint = PROPERTY_HINT_FLAGS; + pinfo.hint_string = "x,y"; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC3: { + pinfo.type = Variant::INT; + pinfo.hint = PROPERTY_HINT_FLAGS; + pinfo.hint_string = "x,y,z"; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC4: { + pinfo.type = Variant::INT; + pinfo.hint = PROPERTY_HINT_FLAGS; + pinfo.hint_string = "x,y,z,w"; + } break; + case RS::GLOBAL_VAR_TYPE_INT: { + pinfo.type = Variant::INT; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC2: { + pinfo.type = Variant::VECTOR2I; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC3: { + pinfo.type = Variant::VECTOR3I; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC4: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_RECT2I: { + pinfo.type = Variant::RECT2I; + } break; + case RS::GLOBAL_VAR_TYPE_UINT: { + pinfo.type = Variant::INT; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC2: { + pinfo.type = Variant::VECTOR2I; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC3: { + pinfo.type = Variant::VECTOR3I; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC4: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_FLOAT: { + pinfo.type = Variant::FLOAT; + } break; + case RS::GLOBAL_VAR_TYPE_VEC2: { + pinfo.type = Variant::VECTOR2; + } break; + case RS::GLOBAL_VAR_TYPE_VEC3: { + pinfo.type = Variant::VECTOR3; + } break; + case RS::GLOBAL_VAR_TYPE_VEC4: { + pinfo.type = Variant::PLANE; + } break; + case RS::GLOBAL_VAR_TYPE_RECT2: { + pinfo.type = Variant::RECT2; + } break; + case RS::GLOBAL_VAR_TYPE_COLOR: { + pinfo.type = Variant::COLOR; + } break; + case RS::GLOBAL_VAR_TYPE_MAT2: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_MAT3: { + pinfo.type = Variant::BASIS; + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { + pinfo.type = Variant::TRANSFORM2D; + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM: { + pinfo.type = Variant::TRANSFORM; + } break; + case RS::GLOBAL_VAR_TYPE_MAT4: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER2D: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Texture2D"; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Texture2DArray"; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Texture3D"; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Cubemap"; + } break; + default: { + + } break; + } + + p_list->push_back(pinfo); + } + } + +public: + bool block_update = false; + + ShaderGlobalsEditorInterface() { + } +}; + +static Variant create_var(RS::GlobalVariableType p_type) { + switch (p_type) { + case RS::GLOBAL_VAR_TYPE_BOOL: { + return false; + } + case RS::GLOBAL_VAR_TYPE_BVEC2: { + return 0; //bits + } + case RS::GLOBAL_VAR_TYPE_BVEC3: { + return 0; //bits + } + case RS::GLOBAL_VAR_TYPE_BVEC4: { + return 0; //bits + } + case RS::GLOBAL_VAR_TYPE_INT: { + return 0; //bits + } + case RS::GLOBAL_VAR_TYPE_IVEC2: { + return Vector2i(); + } + case RS::GLOBAL_VAR_TYPE_IVEC3: { + return Vector3i(); + } + case RS::GLOBAL_VAR_TYPE_IVEC4: { + Vector v4; + v4.resize(4); + v4.write[0] = 0; + v4.write[1] = 0; + v4.write[2] = 0; + v4.write[3] = 0; + return v4; + } + case RS::GLOBAL_VAR_TYPE_RECT2I: { + return Rect2i(); + } + case RS::GLOBAL_VAR_TYPE_UINT: { + return 0; + } + case RS::GLOBAL_VAR_TYPE_UVEC2: { + return Vector2i(); + } + case RS::GLOBAL_VAR_TYPE_UVEC3: { + return Vector3i(); + } + case RS::GLOBAL_VAR_TYPE_UVEC4: { + return Rect2i(); + } + case RS::GLOBAL_VAR_TYPE_FLOAT: { + return 0.0; + } + case RS::GLOBAL_VAR_TYPE_VEC2: { + return Vector2(); + } + case RS::GLOBAL_VAR_TYPE_VEC3: { + return Vector3(); + } + case RS::GLOBAL_VAR_TYPE_VEC4: { + return Plane(); + } + case RS::GLOBAL_VAR_TYPE_RECT2: { + return Rect2(); + } + case RS::GLOBAL_VAR_TYPE_COLOR: { + return Color(); + } + case RS::GLOBAL_VAR_TYPE_MAT2: { + Vector xform; + xform.resize(4); + xform.write[0] = 1; + xform.write[1] = 0; + xform.write[2] = 0; + xform.write[3] = 1; + return xform; + } + case RS::GLOBAL_VAR_TYPE_MAT3: { + return Basis(); + } + case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { + return Transform2D(); + } + case RS::GLOBAL_VAR_TYPE_TRANSFORM: { + return Transform(); + } + case RS::GLOBAL_VAR_TYPE_MAT4: { + Vector xform; + xform.resize(4); + xform.write[0] = 1; + xform.write[1] = 0; + xform.write[2] = 0; + xform.write[3] = 0; + + xform.write[4] = 0; + xform.write[5] = 1; + xform.write[6] = 0; + xform.write[7] = 0; + + xform.write[8] = 0; + xform.write[9] = 0; + xform.write[10] = 1; + xform.write[11] = 0; + + xform.write[12] = 0; + xform.write[13] = 0; + xform.write[14] = 0; + xform.write[15] = 1; + + return xform; + } + case RS::GLOBAL_VAR_TYPE_SAMPLER2D: { + return ""; + } + case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { + return ""; + } + case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { + return ""; + } + case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { + return ""; + } + default: { + return Variant(); + } + } +} + +void ShaderGlobalsEditor::_variable_added() { + + String var = variable_name->get_text().strip_edges(); + if (var == "" || !var.is_valid_identifier()) { + EditorNode::get_singleton()->show_warning(TTR("Please specify a valid variable identifier name.")); + return; + } + + if (RenderingServer::get_singleton()->global_variable_get(var).get_type() != Variant::NIL) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Global variable '%s' already exists'"), var)); + return; + } + + List keywords; + ShaderLanguage::get_keyword_list(&keywords); + + if (keywords.find(var) != nullptr || var == "script") { + EditorNode::get_singleton()->show_warning(vformat(TTR("Name '%s' is a reserved shader language keyword."), var)); + return; + } + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + Variant value = create_var(RS::GlobalVariableType(variable_type->get_selected())); + + undo_redo->create_action("Add Shader Global Variable"); + undo_redo->add_do_method(RS::get_singleton(), "global_variable_add", var, RS::GlobalVariableType(variable_type->get_selected()), value); + undo_redo->add_undo_method(RS::get_singleton(), "global_variable_remove", var); + Dictionary gv; + gv["type"] = global_var_type_names[variable_type->get_selected()]; + gv["value"] = value; + + undo_redo->add_do_property(ProjectSettings::get_singleton(), "shader_globals/" + var, gv); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "shader_globals/" + var, Variant()); + undo_redo->add_do_method(this, "_changed"); + undo_redo->add_undo_method(this, "_changed"); + undo_redo->commit_action(); +} + +void ShaderGlobalsEditor::_variable_deleted(const String &p_variable) { + + print_line("deleted " + p_variable); + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action("Add Shader Global Variable"); + undo_redo->add_do_method(RS::get_singleton(), "global_variable_remove", p_variable); + undo_redo->add_undo_method(RS::get_singleton(), "global_variable_add", p_variable, RS::get_singleton()->global_variable_get_type(p_variable), RS::get_singleton()->global_variable_get(p_variable)); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), "shader_globals/" + p_variable, Variant()); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "shader_globals/" + p_variable, ProjectSettings::get_singleton()->get("shader_globals/" + p_variable)); + undo_redo->add_do_method(this, "_changed"); + undo_redo->add_undo_method(this, "_changed"); + undo_redo->commit_action(); +} + +void ShaderGlobalsEditor::_changed() { + emit_signal("globals_changed"); + if (!interface->block_update) { + interface->_change_notify(); + } +} + +void ShaderGlobalsEditor::_bind_methods() { + ClassDB::bind_method("_changed", &ShaderGlobalsEditor::_changed); + ADD_SIGNAL(MethodInfo("globals_changed")); +} + +void ShaderGlobalsEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (is_visible_in_tree()) { + print_line("OK load settings in globalseditor"); + inspector->edit(interface); + } + } +} + +ShaderGlobalsEditor::ShaderGlobalsEditor() { + + HBoxContainer *add_menu_hb = memnew(HBoxContainer); + add_child(add_menu_hb); + + add_menu_hb->add_child(memnew(Label(TTR("Name:")))); + variable_name = memnew(LineEdit); + variable_name->set_h_size_flags(SIZE_EXPAND_FILL); + add_menu_hb->add_child(variable_name); + + add_menu_hb->add_child(memnew(Label(TTR("Type:")))); + variable_type = memnew(OptionButton); + variable_type->set_h_size_flags(SIZE_EXPAND_FILL); + add_menu_hb->add_child(variable_type); + + for (int i = 0; i < RS::GLOBAL_VAR_TYPE_MAX; i++) { + variable_type->add_item(global_var_type_names[i]); + } + + variable_add = memnew(Button(TTR("Add"))); + add_menu_hb->add_child(variable_add); + variable_add->connect("pressed", callable_mp(this, &ShaderGlobalsEditor::_variable_added)); + + inspector = memnew(EditorInspector); + inspector->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(inspector); + inspector->set_use_wide_editors(true); + inspector->set_enable_capitalize_paths(false); + inspector->set_use_deletable_properties(true); + inspector->connect("property_deleted", callable_mp(this, &ShaderGlobalsEditor::_variable_deleted), varray(), CONNECT_DEFERRED); + + interface = memnew(ShaderGlobalsEditorInterface); + interface->connect("var_changed", Callable(this, "_changed")); +} +ShaderGlobalsEditor::~ShaderGlobalsEditor() { + inspector->edit(NULL); + memdelete(interface); +} diff --git a/editor/shader_globals_editor.h b/editor/shader_globals_editor.h new file mode 100644 index 0000000000..59cdeddd8d --- /dev/null +++ b/editor/shader_globals_editor.h @@ -0,0 +1,38 @@ +#ifndef SHADER_GLOBALS_EDITOR_H +#define SHADER_GLOBALS_EDITOR_H + +#include "core/undo_redo.h" +#include "editor/editor_autoload_settings.h" +#include "editor/editor_data.h" +#include "editor/editor_plugin_settings.h" +#include "editor/editor_sectioned_inspector.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/tab_container.h" + +class ShaderGlobalsEditorInterface; + +class ShaderGlobalsEditor : public VBoxContainer { + + GDCLASS(ShaderGlobalsEditor, VBoxContainer) + + ShaderGlobalsEditorInterface *interface; + EditorInspector *inspector; + + LineEdit *variable_name; + OptionButton *variable_type; + Button *variable_add; + + void _variable_added(); + void _variable_deleted(const String &p_variable); + void _changed(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + ShaderGlobalsEditor(); + ~ShaderGlobalsEditor(); +}; + +#endif // SHADER_GLOBALS_EDITOR_H diff --git a/main/main.cpp b/main/main.cpp index a95af44215..c625a9cb36 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1481,6 +1481,15 @@ Error Main::setup2(Thread::ID p_main_tid_override) { // We could add more, and make the CLI arg require a comma-separated list of profilers. EngineDebugger::get_singleton()->profiler_enable("scripts", true); } + + if (!project_manager) { + // If not running the project manager, and now that the engine is + // able to load resources, load the global shader variables. + // If running on editor, dont load the textures because the editor + // may want to import them first. Editor will reload those later. + rendering_server->global_variables_load_settings(!editor); + } + _start_success = true; locale = String(); @@ -2280,6 +2289,9 @@ void Main::cleanup() { // Sync pending commands that may have been queued from a different thread during ScriptServer finalization RenderingServer::get_singleton()->sync(); + //clear global shader variables before scene and other graphics stuff is deinitialized. + rendering_server->global_variables_clear(); + #ifdef TOOLS_ENABLED EditorNode::unregister_editor_types(); #endif diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp index 1e85f7f1d2..dbe2da86cf 100644 --- a/main/tests/test_shader_lang.cpp +++ b/main/tests/test_shader_lang.cpp @@ -342,7 +342,7 @@ MainLoop *test() { Set types; types.insert("spatial"); - Error err = sl.compile(code, dt, rm, types); + Error err = sl.compile(code, dt, rm, types, NULL); if (err) { diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 775a9b76e2..604bc53422 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -217,6 +217,62 @@ float GeometryInstance3D::get_lod_max_hysteresis() const { void GeometryInstance3D::_notification(int p_what) { } +const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringName p_name) const { + StringName *r = instance_uniform_property_remap.getptr(p_name); + if (!r) { + String s = p_name; + if (s.begins_with("shader_params/")) { + StringName name = s.replace("shader_params/", ""); + instance_uniform_property_remap[p_name] = name; + return instance_uniform_property_remap.getptr(p_name); + } + + return nullptr; + } + + return r; +} + +bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value) { + const StringName *r = _instance_uniform_get_remap(p_name); + if (r) { + set_shader_instance_uniform(*r, p_value); + return true; + } + + return false; +} + +bool GeometryInstance3D::_get(const StringName &p_name, Variant &r_ret) const { + const StringName *r = _instance_uniform_get_remap(p_name); + if (r) { + r_ret = get_shader_instance_uniform(*r); + return true; + } + + return false; +} +void GeometryInstance3D::_get_property_list(List *p_list) const { + List pinfo; + RS::get_singleton()->instance_geometry_get_shader_parameter_list(get_instance(), &pinfo); + for (List::Element *E = pinfo.front(); E; E = E->next()) { + PropertyInfo pi = E->get(); + bool has_def_value = false; + Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), pi.name); + if (def_value.get_type() != Variant::NIL) { + has_def_value = true; + } + if (instance_uniforms.has(pi.name)) { + pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : 0); + } else { + pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : 0); //do not save if not changed + } + + pi.name = "shader_params/" + pi.name; + p_list->push_back(pi); + } +} + void GeometryInstance3D::set_flag(Flags p_flag, bool p_value) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); @@ -258,6 +314,22 @@ float GeometryInstance3D::get_extra_cull_margin() const { return extra_cull_margin; } +void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value) { + + if (p_value.get_type() == Variant::NIL) { + Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_uniform); + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, def_value); + instance_uniforms.erase(p_value); + } else { + instance_uniforms[p_uniform] = p_value; + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, p_value); + } +} + +Variant GeometryInstance3D::get_shader_instance_uniform(const StringName &p_uniform) const { + + return RS::get_singleton()->instance_geometry_get_shader_parameter(get_instance(), p_uniform); +} void GeometryInstance3D::set_custom_aabb(AABB aabb) { RS::get_singleton()->instance_set_custom_aabb(get_instance(), aabb); @@ -280,6 +352,9 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance); ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance); + ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis); ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 9476c28848..cc5f92066f 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -108,9 +108,18 @@ private: float lod_min_hysteresis; float lod_max_hysteresis; + mutable HashMap instance_uniforms; + mutable HashMap instance_uniform_property_remap; + float extra_cull_margin; + const StringName *_instance_uniform_get_remap(const StringName p_name) const; + protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + void _notification(int p_what); static void _bind_methods(); @@ -139,6 +148,9 @@ public: void set_extra_cull_margin(float p_margin); float get_extra_cull_margin() const; + void set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value); + Variant get_shader_instance_uniform(const StringName &p_uniform) const; + void set_custom_aabb(AABB aabb); GeometryInstance3D(); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp new file mode 100644 index 0000000000..13582cf655 --- /dev/null +++ b/scene/main/shader_globals_override.cpp @@ -0,0 +1,257 @@ +#include "shader_globals_override.h" + +#include "core/core_string_names.h" +#include "scene/main/window.h" +#include "scene/scene_string_names.h" + +StringName *ShaderGlobalsOverride::_remap(const StringName &p_name) const { + + StringName *r = param_remaps.getptr(p_name); + if (!r) { + //not cached, do caching + String p = p_name; + if (p.begins_with("params/")) { + String q = p.replace_first("params/", ""); + param_remaps[p] = q; + r = param_remaps.getptr(q); + } + } + + return r; +} +bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_value) { + + StringName *r = _remap(p_name); + + if (r) { + Override *o = overrides.getptr(*r); + if (!o) { + Override ov; + ov.in_use = false; + overrides[*r] = ov; + o = overrides.getptr(*r); + } + if (o) { + o->override = p_value; + if (active) { + RS::get_singleton()->global_variable_set_override(*r, p_value); + } + o->in_use = p_value.get_type() != Variant::NIL; + return true; + } + } + + return false; +} + +bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const { + + StringName *r = _remap(p_name); + + if (r) { + const Override *o = overrides.getptr(*r); + if (o) { + r_ret = o->override; + return true; + } + } + + return false; +} + +void ShaderGlobalsOverride::_get_property_list(List *p_list) const { + + Vector variables; + variables = RS::get_singleton()->global_variable_get_list(); + for (int i = 0; i < variables.size(); i++) { + PropertyInfo pinfo; + pinfo.name = "params/" + variables[i]; + pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + + switch (RS::get_singleton()->global_variable_get_type(variables[i])) { + case RS::GLOBAL_VAR_TYPE_BOOL: { + pinfo.type = Variant::BOOL; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC2: { + pinfo.type = Variant::INT; + pinfo.hint = PROPERTY_HINT_FLAGS; + pinfo.hint_string = "x,y"; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC3: { + pinfo.type = Variant::INT; + pinfo.hint = PROPERTY_HINT_FLAGS; + pinfo.hint_string = "x,y,z"; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC4: { + pinfo.type = Variant::INT; + pinfo.hint = PROPERTY_HINT_FLAGS; + pinfo.hint_string = "x,y,z,w"; + } break; + case RS::GLOBAL_VAR_TYPE_INT: { + pinfo.type = Variant::INT; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC2: { + pinfo.type = Variant::VECTOR2I; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC3: { + pinfo.type = Variant::VECTOR3I; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC4: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_RECT2I: { + pinfo.type = Variant::RECT2I; + } break; + case RS::GLOBAL_VAR_TYPE_UINT: { + pinfo.type = Variant::INT; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC2: { + pinfo.type = Variant::VECTOR2I; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC3: { + pinfo.type = Variant::VECTOR3I; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC4: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_FLOAT: { + pinfo.type = Variant::FLOAT; + } break; + case RS::GLOBAL_VAR_TYPE_VEC2: { + pinfo.type = Variant::VECTOR2; + } break; + case RS::GLOBAL_VAR_TYPE_VEC3: { + pinfo.type = Variant::VECTOR3; + } break; + case RS::GLOBAL_VAR_TYPE_VEC4: { + pinfo.type = Variant::PLANE; + } break; + case RS::GLOBAL_VAR_TYPE_RECT2: { + pinfo.type = Variant::RECT2; + } break; + case RS::GLOBAL_VAR_TYPE_COLOR: { + pinfo.type = Variant::COLOR; + } break; + case RS::GLOBAL_VAR_TYPE_MAT2: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_MAT3: { + pinfo.type = Variant::BASIS; + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { + pinfo.type = Variant::TRANSFORM2D; + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM: { + pinfo.type = Variant::TRANSFORM; + } break; + case RS::GLOBAL_VAR_TYPE_MAT4: { + pinfo.type = Variant::PACKED_INT32_ARRAY; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER2D: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Texture2D"; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Texture2DArray"; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Texture3D"; + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "Cubemap"; + } break; + default: { + + } break; + } + + if (!overrides.has(variables[i])) { + Override o; + o.in_use = false; + Callable::CallError ce; + o.override = Variant::construct(pinfo.type, NULL, 0, ce); + overrides[variables[i]] = o; + } + + Override *o = overrides.getptr(variables[i]); + if (o->in_use && o->override.get_type() != Variant::NIL) { + pinfo.usage |= PROPERTY_USAGE_CHECKED; + pinfo.usage |= PROPERTY_USAGE_STORAGE; + } + + p_list->push_back(pinfo); + } +} + +void ShaderGlobalsOverride::_activate() { + + List nodes; + get_tree()->get_nodes_in_group(SceneStringNames::get_singleton()->shader_overrides_group_active, &nodes); + if (nodes.size() == 0) { + //good we are the only override, enable all + active = true; + add_to_group(SceneStringNames::get_singleton()->shader_overrides_group_active); + + const StringName *K = nullptr; + while ((K = overrides.next(K))) { + Override *o = overrides.getptr(*K); + if (o->in_use && o->override.get_type() != Variant::NIL) { + RS::get_singleton()->global_variable_set_override(*K, o->override); + } + } + + update_configuration_warning(); //may have activated + } +} + +void ShaderGlobalsOverride::_notification(int p_what) { + + if (p_what == Node3D::NOTIFICATION_ENTER_TREE) { + + add_to_group(SceneStringNames::get_singleton()->shader_overrides_group); + _activate(); + + } else if (p_what == Node3D::NOTIFICATION_EXIT_TREE) { + + if (active) { + //remove overrides + const StringName *K = nullptr; + while ((K = overrides.next(K))) { + Override *o = overrides.getptr(*K); + if (o->in_use) { + RS::get_singleton()->global_variable_set_override(*K, Variant()); + } + } + } + + remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active); + remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group); + get_tree()->call_group(SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed + active = false; + } +} + +String ShaderGlobalsOverride::get_configuration_warning() const { + + if (!active) { + return TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."); + } + + return String(); +} + +void ShaderGlobalsOverride::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_activate"), &ShaderGlobalsOverride::_activate); +} + +ShaderGlobalsOverride::ShaderGlobalsOverride() { + active = false; +} diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h new file mode 100644 index 0000000000..33d0dc948f --- /dev/null +++ b/scene/main/shader_globals_override.h @@ -0,0 +1,37 @@ +#ifndef SHADER_GLOBALS_OVERRIDE_H +#define SHADER_GLOBALS_OVERRIDE_H + +#include "scene/3d/node_3d.h" + +class ShaderGlobalsOverride : public Node { + + GDCLASS(ShaderGlobalsOverride, Node); + + struct Override { + bool in_use = false; + Variant override; + }; + + StringName *_remap(const StringName &p_name) const; + + bool active; + mutable HashMap overrides; + mutable HashMap param_remaps; + + void _activate(); + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + + void _notification(int p_what); + static void _bind_methods(); + +public: + String get_configuration_warning() const; + + ShaderGlobalsOverride(); +}; + +#endif // SHADER_GLOBALS_OVERRIDE_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index ff49dbdc8f..264295325f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -177,6 +177,8 @@ #include "scene/3d/node_3d.h" #include "scene/3d/skeleton_3d.h" +#include "scene/main/shader_globals_override.h" + #ifndef _3D_DISABLED #include "scene/3d/area_3d.h" #include "scene/3d/audio_stream_player_3d.h" @@ -403,6 +405,8 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); //can be used in any shader + OS::get_singleton()->yield(); //may take time to init #ifndef _3D_DISABLED diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index ad996e7d50..5e3f8b803b 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -202,4 +202,7 @@ SceneStringNames::SceneStringNames() { parameters_base_path = "parameters/"; tracks_changed = "tracks_changed"; + + shader_overrides_group = StaticCString::create("_shader_overrides_group_"); + shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_"); } diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 58e8c28454..c5de10a6f6 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -207,6 +207,8 @@ public: StringName window_input; StringName theme_changed; + StringName shader_overrides_group; + StringName shader_overrides_group_active; enum { MAX_MATERIALS = 32 diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 5da9c2aeea..955241e79c 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -167,6 +167,17 @@ public: AABB aabb; AABB transformed_aabb; + struct InstanceShaderParameter { + int32_t index = -1; + Variant value; + Variant default_value; + PropertyInfo info; + }; + + Map instance_shader_parameters; + bool instance_allocated_shader_parameters = false; + int32_t instance_allocated_shader_parameters_offset = -1; + virtual void dependency_deleted(RID p_dependency) = 0; virtual void dependency_changed(bool p_aabb, bool p_dependencies) = 0; @@ -357,6 +368,14 @@ public: virtual bool material_is_animated(RID p_material) = 0; virtual bool material_casts_shadows(RID p_material) = 0; + struct InstanceShaderParam { + PropertyInfo info; + int index; + Variant default_value; + }; + + virtual void material_get_instance_shader_parameters(RID p_material, List *r_parameters) = 0; + virtual void material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance) = 0; /* MESH API */ @@ -635,6 +654,24 @@ public: virtual int particles_get_draw_passes(RID p_particles) const = 0; virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const = 0; + /* GLOBAL VARIABLES */ + + virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) = 0; + virtual void global_variable_remove(const StringName &p_name) = 0; + virtual Vector global_variable_get_list() const = 0; + + virtual void global_variable_set(const StringName &p_name, const Variant &p_value) = 0; + virtual void global_variable_set_override(const StringName &p_name, const Variant &p_value) = 0; + virtual Variant global_variable_get(const StringName &p_name) const = 0; + virtual RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const = 0; + + virtual void global_variables_load_settings(bool p_load_textures = true) = 0; + virtual void global_variables_clear() = 0; + + virtual int32_t global_variables_instance_allocate(RID p_instance) = 0; + virtual void global_variables_instance_free(RID p_instance) = 0; + virtual void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) = 0; + /* RENDER TARGET */ enum RenderTargetFlags { diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp index ba4f4c4acb..1a21fdb4d7 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp @@ -618,6 +618,14 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ } } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 7; + u.ids.push_back(storage->global_variables_get_storage_buffer()); + uniforms.push_back(u); + } + //validate and update lighs if they are being used if (light_count > 0) { @@ -2012,6 +2020,9 @@ void RasterizerCanvasRD::ShaderData::get_param_list(List *p_param_ for (Map::Element *E = uniforms.front(); E; E = E->next()) { + if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + continue; + } if (E->get().texture_order >= 0) { order[E->get().texture_order + 100000] = E->key(); } else { @@ -2027,6 +2038,23 @@ void RasterizerCanvasRD::ShaderData::get_param_list(List *p_param_ } } +void RasterizerCanvasRD::ShaderData::get_instance_param_list(List *p_param_list) const { + + for (Map::Element *E = uniforms.front(); E; E = E->next()) { + + if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RasterizerStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E->get()); + p.info.name = E->key(); //supply name + p.index = E->get().instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E->get().default_value, E->get().type, E->get().hint); + p_param_list->push_back(p); + } +} + bool RasterizerCanvasRD::ShaderData::is_param_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; @@ -2366,6 +2394,8 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { actions.default_repeat = ShaderLanguage::REPEAT_DISABLE; actions.base_varying_index = 4; + actions.global_buffer_array_variable = "global_variables.data"; + shader.compiler.initialize(actions); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h index 83b431eaf6..4d47b3e13b 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h @@ -185,6 +185,8 @@ class RasterizerCanvasRD : public RasterizerCanvas { virtual void set_code(const String &p_Code); virtual void set_default_texture_param(const StringName &p_name, RID p_texture); virtual void get_param_list(List *p_param_list) const; + virtual void get_instance_param_list(List *p_param_list) const; + virtual bool is_param_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index 1364407f70..b3cf40f166 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -378,6 +378,10 @@ void RasterizerSceneHighEndRD::ShaderData::get_param_list(List *p_ for (Map::Element *E = uniforms.front(); E; E = E->next()) { + if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + continue; + } + if (E->get().texture_order >= 0) { order[E->get().texture_order + 100000] = E->key(); } else { @@ -393,6 +397,23 @@ void RasterizerSceneHighEndRD::ShaderData::get_param_list(List *p_ } } +void RasterizerSceneHighEndRD::ShaderData::get_instance_param_list(List *p_param_list) const { + + for (Map::Element *E = uniforms.front(); E; E = E->next()) { + + if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RasterizerStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E->get()); + p.info.name = E->key(); //supply name + p.index = E->get().instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E->get().default_value, E->get().type, E->get().hint); + p_param_list->push_back(p); + } +} + bool RasterizerSceneHighEndRD::ShaderData::is_param_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; @@ -828,6 +849,7 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, store_transform(Transform(e->instance->transform.basis.inverse().transposed()), id.normal_transform); id.flags = 0; id.mask = e->instance->layer_mask; + id.instance_uniforms_ofs = e->instance->instance_allocated_shader_parameters_offset >= 0 ? e->instance->instance_allocated_shader_parameters_offset : 0; if (e->instance->base_type == RS::INSTANCE_MULTIMESH) { id.flags |= INSTANCE_DATA_FLAG_MULTIMESH; @@ -2707,6 +2729,14 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() { uniforms.push_back(u); } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 16; + u.ids.push_back(storage->global_variables_get_storage_buffer()); + uniforms.push_back(u); + } + render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, SCENE_UNIFORM_SET); } } @@ -3083,6 +3113,8 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_variables.data"; + actions.instance_uniform_index_variable = "instances.data[instance_index].instance_uniforms_ofs"; shader.compiler.initialize(actions); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h index 83ff46ca7e..a48e2e2259 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h @@ -152,6 +152,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { virtual void set_code(const String &p_Code); virtual void set_default_texture_param(const StringName &p_name, RID p_texture); virtual void get_param_list(List *p_param_list) const; + void get_instance_param_list(List *p_param_list) const; + virtual bool is_param_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; @@ -361,7 +363,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { float transform[16]; float normal_transform[16]; uint32_t flags; - uint32_t instance_ofs; //instance_offset in instancing/skeleton buffer + uint32_t instance_uniforms_ofs; //instance_offset in instancing/skeleton buffer uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap) uint32_t mask; }; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp index 53dbe8b90f..8877de87ac 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -931,6 +931,10 @@ void RasterizerSceneRD::SkyShaderData::get_param_list(List *p_para for (Map::Element *E = uniforms.front(); E; E = E->next()) { + if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + if (E->get().texture_order >= 0) { order[E->get().texture_order + 100000] = E->key(); } else { @@ -946,6 +950,23 @@ void RasterizerSceneRD::SkyShaderData::get_param_list(List *p_para } } +void RasterizerSceneRD::SkyShaderData::get_instance_param_list(List *p_param_list) const { + + for (Map::Element *E = uniforms.front(); E; E = E->next()) { + + if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RasterizerStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E->get()); + p.info.name = E->key(); //supply name + p.index = E->get().instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E->get().default_value, E->get().type, E->get().hint); + p_param_list->push_back(p); + } +} + bool RasterizerSceneRD::SkyShaderData::is_param_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; @@ -4226,6 +4247,7 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_variables.data"; sky_shader.compiler.initialize(actions); } @@ -4263,6 +4285,14 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { uniforms.push_back(u); } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 1; + u.ids.push_back(storage->global_variables_get_storage_buffer()); + uniforms.push_back(u); + } + sky_scene_state.sampler_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_SAMPLERS); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index 3955b55ca6..a511838e16 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -191,6 +191,7 @@ private: virtual void set_code(const String &p_Code); virtual void set_default_texture_param(const StringName &p_name, RID p_texture); virtual void get_param_list(List *p_param_list) const; + virtual void get_instance_param_list(List *p_param_list) const; virtual bool is_param_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index 148494692c..97ef604dd2 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -31,6 +31,7 @@ #include "rasterizer_storage_rd.h" #include "core/engine.h" +#include "core/io/resource_loader.h" #include "core/project_settings.h" #include "servers/rendering/shader_language.h" @@ -921,6 +922,7 @@ void RasterizerStorageRD::shader_set_code(RID p_shader, const String &p_code) { Material *material = E->get(); if (shader->data) { material->data = material_data_request_func[new_type](shader->data); + material->data->self = material->self; material->data->set_next_pass(material->next_pass); material->data->set_render_priority(material->priority); } @@ -1021,8 +1023,8 @@ void RasterizerStorageRD::_material_queue_update(Material *material, bool p_unif material->update_next = material_update_list; material_update_list = material; material->update_requested = true; - material->uniform_dirty = p_uniform; - material->texture_dirty = p_texture; + material->uniform_dirty = material->uniform_dirty || p_uniform; + material->texture_dirty = material->texture_dirty || p_texture; } void RasterizerStorageRD::material_set_shader(RID p_material, RID p_shader) { @@ -1059,6 +1061,7 @@ void RasterizerStorageRD::material_set_shader(RID p_material, RID p_shader) { ERR_FAIL_COND(shader->data == nullptr); material->data = material_data_request_func[shader->type](shader->data); + material->data->self = p_material; material->data->set_next_pass(material->next_pass); material->data->set_render_priority(material->priority); //updating happens later @@ -1144,6 +1147,19 @@ bool RasterizerStorageRD::material_casts_shadows(RID p_material) { return true; //by default everything casts shadows } +void RasterizerStorageRD::material_get_instance_shader_parameters(RID p_material, List *r_parameters) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + if (material->shader && material->shader->data) { + material->shader->data->get_instance_param_list(r_parameters); + + if (material->next_pass.is_valid()) { + material_get_instance_shader_parameters(material->next_pass, r_parameters); + } + } +} + void RasterizerStorageRD::material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance) { Material *material = material_owner.getornull(p_material); ERR_FAIL_COND(!material); @@ -1631,11 +1647,36 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, void RasterizerStorageRD::MaterialData::update_uniform_buffer(const Map &p_uniforms, const uint32_t *p_uniform_offsets, const Map &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) { + bool uses_global_buffer = false; + for (Map::Element *E = p_uniforms.front(); E; E = E->next()) { if (E->get().order < 0) continue; // texture, does not go here + if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; //instance uniforms don't appear in the bufferr + } + + if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) { + //this is a global variable, get the index to it + RasterizerStorageRD *rs = base_singleton; + + GlobalVariables::Variable *gv = rs->global_variables.variables.getptr(E->key()); + uint32_t index = 0; + if (gv) { + index = gv->buffer_index; + } else { + WARN_PRINT("Shader uses global uniform '" + E->key() + "', but it was removed at some point. Material will not display correctly."); + } + + uint32_t offset = p_uniform_offsets[E->get().order]; + uint32_t *intptr = (uint32_t *)&p_buffer[offset]; + *intptr = index; + uses_global_buffer = true; + continue; + } + //regular uniform uint32_t offset = p_uniform_offsets[E->get().order]; #ifdef DEBUG_ENABLED @@ -1664,6 +1705,38 @@ void RasterizerStorageRD::MaterialData::update_uniform_buffer(const Mapglobal_variables.materials_using_buffer.push_back(self); + } else { + rs->global_variables.materials_using_buffer.erase(global_buffer_E); + global_buffer_E = nullptr; + } + } +} + +RasterizerStorageRD::MaterialData::~MaterialData() { + if (global_buffer_E) { + //unregister global buffers + RasterizerStorageRD *rs = base_singleton; + rs->global_variables.materials_using_buffer.erase(global_buffer_E); + } + + if (global_texture_E) { + //unregister global textures + RasterizerStorageRD *rs = base_singleton; + + for (Map::Element *E = used_global_textures.front(); E; E = E->next()) { + GlobalVariables::Variable *v = rs->global_variables.variables.getptr(E->key()); + if (v) { + v->texture_materials.erase(self); + } + } + //unregister material from those using global textures + rs->global_variables.materials_using_texture.erase(global_texture_E); + } } void RasterizerStorageRD::MaterialData::update_textures(const Map &p_parameters, const Map &p_default_textures, const Vector &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) { @@ -1675,22 +1748,57 @@ void RasterizerStorageRD::MaterialData::update_textures(const Map::Element *V = p_parameters.find(uniform_name); - if (V) { - texture = V->get(); - } + if (p_texture_uniforms[i].global) { - if (!texture.is_valid()) { - const Map::Element *W = p_default_textures.find(uniform_name); - if (W) { + RasterizerStorageRD *rs = base_singleton; - texture = W->get(); + uses_global_textures = true; + + GlobalVariables::Variable *v = rs->global_variables.variables.getptr(uniform_name); + if (v) { + if (v->buffer_index >= 0) { + WARN_PRINT("Shader uses global uniform texture '" + String(uniform_name) + "', but it changed type and is no longer a texture!."); + + } else { + + Map::Element *E = used_global_textures.find(uniform_name); + if (!E) { + E = used_global_textures.insert(uniform_name, global_textures_pass); + v->texture_materials.insert(self); + } else { + E->get() = global_textures_pass; + } + + texture = v->override.get_type() != Variant::NIL ? v->override : v->value; + } + + } else { + WARN_PRINT("Shader uses global uniform texture '" + String(uniform_name) + "', but it was removed at some point. Material will not display correctly."); + } + } else { + if (!texture.is_valid()) { + + const Map::Element *V = p_parameters.find(uniform_name); + if (V) { + texture = V->get(); + } + } + + if (!texture.is_valid()) { + const Map::Element *W = p_default_textures.find(uniform_name); + if (W) { + + texture = W->get(); + } } } @@ -1753,6 +1861,36 @@ void RasterizerStorageRD::MaterialData::update_textures(const Mapdetect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel); } #endif + { + //for textures no longer used, unregister them + List::Element *> to_delete; + RasterizerStorageRD *rs = base_singleton; + + for (Map::Element *E = used_global_textures.front(); E; E = E->next()) { + if (E->get() != global_textures_pass) { + to_delete.push_back(E); + + GlobalVariables::Variable *v = rs->global_variables.variables.getptr(E->key()); + if (v) { + v->texture_materials.erase(self); + } + } + } + + while (to_delete.front()) { + used_global_textures.erase(to_delete.front()->get()); + to_delete.pop_front(); + } + //handle registering/unregistering global textures + if (uses_global_textures != (global_texture_E != nullptr)) { + if (uses_global_textures) { + global_texture_E = rs->global_variables.materials_using_texture.push_back(self); + } else { + rs->global_variables.materials_using_texture.erase(global_texture_E); + global_texture_E = nullptr; + } + } + } } void RasterizerStorageRD::material_force_update_textures(RID p_material, ShaderType p_shader_type) { @@ -4627,7 +4765,685 @@ void RasterizerStorageRD::_update_decal_atlas() { } } +int32_t RasterizerStorageRD::_global_variable_allocate(uint32_t p_elements) { + + int32_t idx = 0; + while (idx + p_elements <= global_variables.buffer_size) { + if (global_variables.buffer_usage[idx].elements == 0) { + bool valid = true; + for (uint32_t i = 1; i < p_elements; i++) { + if (global_variables.buffer_usage[idx + i].elements > 0) { + valid = false; + idx += i + global_variables.buffer_usage[idx + i].elements; + break; + } + } + + if (!valid) { + continue; //if not valid, idx is in new position + } + + return idx; + } else { + idx += global_variables.buffer_usage[idx].elements; + } + } + + return -1; +} + +void RasterizerStorageRD::_global_variable_store_in_buffer(int32_t p_index, RS::GlobalVariableType p_type, const Variant &p_value) { + + switch (p_type) { + case RS::GLOBAL_VAR_TYPE_BOOL: { + + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + bool b = p_value; + bv.x = b ? 1.0 : 0.0; + bv.y = 0.0; + bv.z = 0.0; + bv.w = 0.0; + + } break; + case RS::GLOBAL_VAR_TYPE_BVEC2: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + uint32_t bvec = p_value; + bv.x = (bvec & 1) ? 1.0 : 0.0; + bv.y = (bvec & 2) ? 1.0 : 0.0; + bv.z = 0.0; + bv.w = 0.0; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC3: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + uint32_t bvec = p_value; + bv.x = (bvec & 1) ? 1.0 : 0.0; + bv.y = (bvec & 2) ? 1.0 : 0.0; + bv.z = (bvec & 4) ? 1.0 : 0.0; + bv.w = 0.0; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC4: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + uint32_t bvec = p_value; + bv.x = (bvec & 1) ? 1.0 : 0.0; + bv.y = (bvec & 2) ? 1.0 : 0.0; + bv.z = (bvec & 4) ? 1.0 : 0.0; + bv.w = (bvec & 8) ? 1.0 : 0.0; + } break; + case RS::GLOBAL_VAR_TYPE_INT: { + GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + int32_t v = p_value; + bv.x = v; + bv.y = 0; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC2: { + GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + Vector2i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC3: { + GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + Vector3i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = v.z; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC4: { + GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + Vector v = p_value; + bv.x = v.size() >= 1 ? v[0] : 0; + bv.y = v.size() >= 2 ? v[1] : 0; + bv.z = v.size() >= 3 ? v[2] : 0; + bv.w = v.size() >= 4 ? v[3] : 0; + } break; + case RS::GLOBAL_VAR_TYPE_RECT2I: { + GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + Rect2i v = p_value; + bv.x = v.position.x; + bv.y = v.position.y; + bv.z = v.size.x; + bv.w = v.size.y; + } break; + case RS::GLOBAL_VAR_TYPE_UINT: { + GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + uint32_t v = p_value; + bv.x = v; + bv.y = 0; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC2: { + GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + Vector2i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC3: { + GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + Vector3i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = v.z; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC4: { + GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + Vector v = p_value; + bv.x = v.size() >= 1 ? v[0] : 0; + bv.y = v.size() >= 2 ? v[1] : 0; + bv.z = v.size() >= 3 ? v[2] : 0; + bv.w = v.size() >= 4 ? v[3] : 0; + } break; + case RS::GLOBAL_VAR_TYPE_FLOAT: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + float v = p_value; + bv.x = v; + bv.y = 0; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_VEC2: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + Vector2 v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_VEC3: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + Vector3 v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = v.z; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_VEC4: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + Plane v = p_value; + bv.x = v.normal.x; + bv.y = v.normal.y; + bv.z = v.normal.z; + bv.w = v.d; + } break; + case RS::GLOBAL_VAR_TYPE_COLOR: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + Color v = p_value; + bv.x = v.r; + bv.y = v.g; + bv.z = v.b; + bv.w = v.a; + + GlobalVariables::Value &bv_linear = global_variables.buffer_values[p_index + 1]; + v = v.to_linear(); + bv_linear.x = v.r; + bv_linear.y = v.g; + bv_linear.z = v.b; + bv_linear.w = v.a; + + } break; + case RS::GLOBAL_VAR_TYPE_RECT2: { + GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + Rect2 v = p_value; + bv.x = v.position.x; + bv.y = v.position.y; + bv.z = v.size.x; + bv.w = v.size.y; + } break; + case RS::GLOBAL_VAR_TYPE_MAT2: { + GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + Vector m2 = p_value; + if (m2.size() < 4) { + m2.resize(4); + } + bv[0].x = m2[0]; + bv[0].y = m2[1]; + bv[0].z = 0; + bv[0].w = 0; + + bv[1].x = m2[2]; + bv[1].y = m2[3]; + bv[1].z = 0; + bv[1].w = 0; + + } break; + case RS::GLOBAL_VAR_TYPE_MAT3: { + + GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + Basis v = p_value; + bv[0].x = v.elements[0][0]; + bv[0].y = v.elements[1][0]; + bv[0].z = v.elements[2][0]; + bv[0].w = 0; + + bv[1].x = v.elements[0][1]; + bv[1].y = v.elements[1][1]; + bv[1].z = v.elements[2][1]; + bv[1].w = 0; + + bv[2].x = v.elements[0][2]; + bv[2].y = v.elements[1][2]; + bv[2].z = v.elements[2][2]; + bv[2].w = 0; + + } break; + case RS::GLOBAL_VAR_TYPE_MAT4: { + + GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + + Vector m2 = p_value; + if (m2.size() < 16) { + m2.resize(16); + } + + bv[0].x = m2[0]; + bv[0].y = m2[1]; + bv[0].z = m2[2]; + bv[0].w = m2[3]; + + bv[1].x = m2[4]; + bv[1].y = m2[5]; + bv[1].z = m2[6]; + bv[1].w = m2[7]; + + bv[2].x = m2[8]; + bv[2].y = m2[9]; + bv[2].z = m2[10]; + bv[2].w = m2[11]; + + bv[3].x = m2[12]; + bv[3].y = m2[13]; + bv[3].z = m2[14]; + bv[3].w = m2[15]; + + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { + + GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + Transform2D v = p_value; + bv[0].x = v.elements[0][0]; + bv[0].y = v.elements[0][1]; + bv[0].z = 0; + bv[0].w = 0; + + bv[1].x = v.elements[1][0]; + bv[1].y = v.elements[1][1]; + bv[1].z = 0; + bv[1].w = 0; + + bv[2].x = v.elements[2][0]; + bv[2].y = v.elements[2][1]; + bv[2].z = 1; + bv[2].w = 0; + + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM: { + + GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + Transform v = p_value; + bv[0].x = v.basis.elements[0][0]; + bv[0].y = v.basis.elements[1][0]; + bv[0].z = v.basis.elements[2][0]; + bv[0].w = 0; + + bv[1].x = v.basis.elements[0][1]; + bv[1].y = v.basis.elements[1][1]; + bv[1].z = v.basis.elements[2][1]; + bv[1].w = 0; + + bv[2].x = v.basis.elements[0][2]; + bv[2].y = v.basis.elements[1][2]; + bv[2].z = v.basis.elements[2][2]; + bv[2].w = 0; + + bv[2].x = v.origin.x; + bv[2].y = v.origin.y; + bv[2].z = v.origin.z; + bv[2].w = 1; + + } break; + default: { + ERR_FAIL(); + } + } +} + +void RasterizerStorageRD::_global_variable_mark_buffer_dirty(int32_t p_index, int32_t p_elements) { + + int32_t prev_chunk = -1; + + for (int32_t i = 0; i < p_elements; i++) { + int32_t chunk = (p_index + i) / GlobalVariables::BUFFER_DIRTY_REGION_SIZE; + if (chunk != prev_chunk) { + if (!global_variables.buffer_dirty_regions[chunk]) { + global_variables.buffer_dirty_regions[chunk] = true; + global_variables.buffer_dirty_region_count++; + } + } + + prev_chunk = chunk; + } +} + +void RasterizerStorageRD::global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) { + + ERR_FAIL_COND(global_variables.variables.has(p_name)); + GlobalVariables::Variable gv; + gv.type = p_type; + gv.value = p_value; + gv.buffer_index = -1; + + if (p_type >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { + //is texture + global_variables.must_update_texture_materials = true; //normally ther are no + } else { + + gv.buffer_elements = 1; + if (p_type == RS::GLOBAL_VAR_TYPE_COLOR || p_type == RS::GLOBAL_VAR_TYPE_MAT2) { + //color needs to elements to store srgb and linear + gv.buffer_elements = 2; + } + if (p_type == RS::GLOBAL_VAR_TYPE_MAT3 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM_2D) { + //color needs to elements to store srgb and linear + gv.buffer_elements = 3; + } + if (p_type == RS::GLOBAL_VAR_TYPE_MAT4 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM) { + //color needs to elements to store srgb and linear + gv.buffer_elements = 4; + } + + //is vector, allocate in buffer and update index + gv.buffer_index = _global_variable_allocate(gv.buffer_elements); + ERR_FAIL_COND_MSG(gv.buffer_index < 0, vformat("Failed allocating global variable '%s' out of buffer memory. Consider increasing it in the Project Settings.", String(p_name))); + global_variables.buffer_usage[gv.buffer_index].elements = gv.buffer_elements; + _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); + _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + + global_variables.must_update_buffer_materials = true; //normally ther are no + } + + global_variables.variables[p_name] = gv; +} + +void RasterizerStorageRD::global_variable_remove(const StringName &p_name) { + if (!global_variables.variables.has(p_name)) { + return; + } + GlobalVariables::Variable &gv = global_variables.variables[p_name]; + + if (gv.buffer_index >= 0) { + global_variables.buffer_usage[gv.buffer_index].elements = 0; + global_variables.must_update_buffer_materials = true; + } else { + global_variables.must_update_texture_materials = true; + } + + global_variables.variables.erase(p_name); +} +Vector RasterizerStorageRD::global_variable_get_list() const { + + if (!Engine::get_singleton()->is_editor_hint()) { + ERR_FAIL_V_MSG(Vector(), "This function should never be used outside the editor, it can severely damage performance."); + } + + const StringName *K = NULL; + Vector names; + while ((K = global_variables.variables.next(K))) { + names.push_back(*K); + } + names.sort_custom(); + return names; +} + +void RasterizerStorageRD::global_variable_set(const StringName &p_name, const Variant &p_value) { + ERR_FAIL_COND(!global_variables.variables.has(p_name)); + GlobalVariables::Variable &gv = global_variables.variables[p_name]; + gv.value = p_value; + if (gv.override.get_type() == Variant::NIL) { + if (gv.buffer_index >= 0) { + //buffer + _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); + _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + } else { + //texture + for (Set::Element *E = gv.texture_materials.front(); E; E = E->next()) { + Material *material = material_owner.getornull(E->get()); + ERR_CONTINUE(!material); + _material_queue_update(material, false, true); + } + } + } +} +void RasterizerStorageRD::global_variable_set_override(const StringName &p_name, const Variant &p_value) { + if (!global_variables.variables.has(p_name)) { + return; //variable may not exist + } + GlobalVariables::Variable &gv = global_variables.variables[p_name]; + + gv.override = p_value; + + if (gv.buffer_index >= 0) { + //buffer + if (gv.override.get_type() == Variant::NIL) { + _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); + } else { + _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.override); + } + + _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + } else { + //texture + //texture + for (Set::Element *E = gv.texture_materials.front(); E; E = E->next()) { + Material *material = material_owner.getornull(E->get()); + ERR_CONTINUE(!material); + _material_queue_update(material, false, true); + } + } +} + +Variant RasterizerStorageRD::global_variable_get(const StringName &p_name) const { + + if (!Engine::get_singleton()->is_editor_hint()) { + ERR_FAIL_V_MSG(Variant(), "This function should never be used outside the editor, it can severely damage performance."); + } + + if (!global_variables.variables.has(p_name)) { + return Variant(); + } + + return global_variables.variables[p_name].value; +} + +RS::GlobalVariableType RasterizerStorageRD::global_variable_get_type_internal(const StringName &p_name) const { + + if (!global_variables.variables.has(p_name)) { + return RS::GLOBAL_VAR_TYPE_MAX; + } + + return global_variables.variables[p_name].type; +} + +RS::GlobalVariableType RasterizerStorageRD::global_variable_get_type(const StringName &p_name) const { + if (!Engine::get_singleton()->is_editor_hint()) { + ERR_FAIL_V_MSG(RS::GLOBAL_VAR_TYPE_MAX, "This function should never be used outside the editor, it can severely damage performance."); + } + + return global_variable_get_type_internal(p_name); +} + +void RasterizerStorageRD::global_variables_load_settings(bool p_load_textures) { + + List settings; + ProjectSettings::get_singleton()->get_property_list(&settings); + + for (List::Element *E = settings.front(); E; E = E->next()) { + if (E->get().name.begins_with("shader_globals/")) { + StringName name = E->get().name.get_slice("/", 1); + Dictionary d = ProjectSettings::get_singleton()->get(E->get().name); + + ERR_CONTINUE(!d.has("type")); + ERR_CONTINUE(!d.has("value")); + + String type = d["type"]; + + static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { + "bool", + "bvec2", + "bvec3", + "bvec4", + "int", + "ivec2", + "ivec3", + "ivec4", + "rect2i", + "uint", + "uvec2", + "uvec3", + "uvec4", + "float", + "vec2", + "vec3", + "vec4", + "color", + "rect2", + "mat2", + "mat3", + "mat4", + "transform_2d", + "transform", + "sampler2D", + "sampler2DArray", + "sampler3D", + "samplerCube", + }; + + RS::GlobalVariableType gvtype = RS::GLOBAL_VAR_TYPE_MAX; + + for (int i = 0; i < RS::GLOBAL_VAR_TYPE_MAX; i++) { + if (global_var_type_names[i] == type) { + gvtype = RS::GlobalVariableType(i); + break; + } + } + + ERR_CONTINUE(gvtype == RS::GLOBAL_VAR_TYPE_MAX); //type invalid + + Variant value = d["value"]; + + if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { + //textire + if (!p_load_textures) { + value = RID(); + continue; + } + + String path = value; + RES resource = ResourceLoader::load(path); + ERR_CONTINUE(resource.is_null()); + value = resource; + } + + if (global_variables.variables.has(name)) { + //has it, update it + global_variable_set(name, value); + } else { + global_variable_add(name, gvtype, value); + } + } + } +} + +void RasterizerStorageRD::global_variables_clear() { + global_variables.variables.clear(); //not right but for now enough +} + +RID RasterizerStorageRD::global_variables_get_storage_buffer() const { + return global_variables.buffer; +} + +int32_t RasterizerStorageRD::global_variables_instance_allocate(RID p_instance) { + ERR_FAIL_COND_V(global_variables.instance_buffer_pos.has(p_instance), -1); + int32_t pos = _global_variable_allocate(ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); + global_variables.instance_buffer_pos[p_instance] = pos; //save anyway + ERR_FAIL_COND_V_MSG(pos < 0, -1, "Too many instances using shader instance variables. Increase buffer size in Project Settings."); + global_variables.buffer_usage[pos].elements = ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES; + return pos; +} + +void RasterizerStorageRD::global_variables_instance_free(RID p_instance) { + ERR_FAIL_COND(!global_variables.instance_buffer_pos.has(p_instance)); + int32_t pos = global_variables.instance_buffer_pos[p_instance]; + if (pos >= 0) { + global_variables.buffer_usage[pos].elements = 0; + } + global_variables.instance_buffer_pos.erase(p_instance); +} +void RasterizerStorageRD::global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) { + + if (!global_variables.instance_buffer_pos.has(p_instance)) { + return; //just not allocated, ignore + } + int32_t pos = global_variables.instance_buffer_pos[p_instance]; + + if (pos < 0) { + return; //again, not allocated, ignore + } + ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); + ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + + ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = { + ShaderLanguage::TYPE_MAX, //nil + ShaderLanguage::TYPE_BOOL, //bool + ShaderLanguage::TYPE_INT, //int + ShaderLanguage::TYPE_FLOAT, //float + ShaderLanguage::TYPE_MAX, //string + ShaderLanguage::TYPE_VEC2, //vec2 + ShaderLanguage::TYPE_IVEC2, //vec2i + ShaderLanguage::TYPE_VEC4, //rect2 + ShaderLanguage::TYPE_IVEC4, //rect2i + ShaderLanguage::TYPE_VEC3, // vec3 + ShaderLanguage::TYPE_IVEC3, //vec3i + ShaderLanguage::TYPE_MAX, //xform2d not supported here + ShaderLanguage::TYPE_VEC4, //plane + ShaderLanguage::TYPE_VEC4, //quat + ShaderLanguage::TYPE_MAX, //aabb not supported here + ShaderLanguage::TYPE_MAX, //basis not supported here + ShaderLanguage::TYPE_MAX, //xform not supported here + ShaderLanguage::TYPE_VEC4 //color + }; + + ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()]; + + ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + + pos += p_index; + + _fill_std140_variant_ubo_value(datatype, p_value, (uint8_t *)&global_variables.buffer_values[pos], true); //instances always use linear color in this renderer + _global_variable_mark_buffer_dirty(pos, 1); +} + +void RasterizerStorageRD::_update_global_variables() { + + if (global_variables.buffer_dirty_region_count > 0) { + uint32_t total_regions = global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE; + if (total_regions / global_variables.buffer_dirty_region_count <= 4) { + // 25% of regions dirty, just update all buffer + RD::get_singleton()->buffer_update(global_variables.buffer, 0, sizeof(GlobalVariables::Value) * global_variables.buffer_size, global_variables.buffer_values); + zeromem(global_variables.buffer_dirty_regions, sizeof(bool) * total_regions); + } else { + uint32_t region_byte_size = sizeof(GlobalVariables::Value) * GlobalVariables::BUFFER_DIRTY_REGION_SIZE; + + for (uint32_t i = 0; i < total_regions; i++) { + if (global_variables.buffer_dirty_regions[i]) { + + RD::get_singleton()->buffer_update(global_variables.buffer, i * region_byte_size, region_byte_size, global_variables.buffer_values); + + global_variables.buffer_dirty_regions[i] = false; + } + } + } + + global_variables.buffer_dirty_region_count = 0; + } + + if (global_variables.must_update_buffer_materials) { + // only happens in the case of a buffer variable added or removed, + // so not often. + for (List::Element *E = global_variables.materials_using_buffer.front(); E; E = E->next()) { + Material *material = material_owner.getornull(E->get()); + ERR_CONTINUE(!material); //wtf + + _material_queue_update(material, true, false); + } + + global_variables.must_update_buffer_materials = false; + } + + if (global_variables.must_update_texture_materials) { + // only happens in the case of a buffer variable added or removed, + // so not often. + for (List::Element *E = global_variables.materials_using_texture.front(); E; E = E->next()) { + Material *material = material_owner.getornull(E->get()); + ERR_CONTINUE(!material); //wtf + + _material_queue_update(material, false, true); + print_line("update material texture?"); + } + + global_variables.must_update_texture_materials = false; + } +} + void RasterizerStorageRD::update_dirty_resources() { + _update_global_variables(); //must do before materials, so it can queue them for update _update_queued_materials(); _update_dirty_multimeshes(); _update_dirty_skeletons(); @@ -4806,12 +5622,27 @@ String RasterizerStorageRD::get_captured_timestamp_name(uint32_t p_index) const return RD::get_singleton()->get_captured_timestamp_name(p_index); } +RasterizerStorageRD *RasterizerStorageRD::base_singleton = nullptr; + RasterizerStorageRD::RasterizerStorageRD() { + base_singleton = this; + for (int i = 0; i < SHADER_TYPE_MAX; i++) { shader_data_request_func[i] = nullptr; } + static_assert(sizeof(GlobalVariables::Value) == 16); + + global_variables.buffer_size = GLOBAL_GET("rendering/high_end/global_shader_variables_buffer_size"); + global_variables.buffer_size = MAX(4096, global_variables.buffer_size); + global_variables.buffer_values = memnew_arr(GlobalVariables::Value, global_variables.buffer_size); + zeromem(global_variables.buffer_values, sizeof(GlobalVariables::Value) * global_variables.buffer_size); + global_variables.buffer_usage = memnew_arr(GlobalVariables::ValueUsage, global_variables.buffer_size); + global_variables.buffer_dirty_regions = memnew_arr(bool, global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); + zeromem(global_variables.buffer_dirty_regions, sizeof(bool) * global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); + global_variables.buffer = RD::get_singleton()->storage_buffer_create(sizeof(GlobalVariables::Value) * global_variables.buffer_size); + material_update_list = nullptr; { //create default textures @@ -5165,6 +5996,11 @@ RasterizerStorageRD::RasterizerStorageRD() { RasterizerStorageRD::~RasterizerStorageRD() { + memdelete_arr(global_variables.buffer_values); + memdelete_arr(global_variables.buffer_usage); + memdelete_arr(global_variables.buffer_dirty_regions); + RD::get_singleton()->free(global_variables.buffer); + //def textures for (int i = 0; i < DEFAULT_RD_TEXTURE_MAX; i++) { RD::get_singleton()->free(default_rd_textures[i]); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index 1980f043a0..f874c3baf8 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -52,6 +52,8 @@ public: virtual void set_code(const String &p_Code) = 0; virtual void set_default_texture_param(const StringName &p_name, RID p_texture) = 0; virtual void get_param_list(List *p_param_list) const = 0; + + virtual void get_instance_param_list(List *p_param_list) const = 0; virtual bool is_param_texture(const StringName &p_param) const = 0; virtual bool is_animated() const = 0; virtual bool casts_shadows() const = 0; @@ -69,7 +71,15 @@ public: virtual void set_render_priority(int p_priority) = 0; virtual void set_next_pass(RID p_pass) = 0; virtual void update_parameters(const Map &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) = 0; - virtual ~MaterialData() {} + virtual ~MaterialData(); + + private: + friend class RasterizerStorageRD; + RID self; + List::Element *global_buffer_E = nullptr; + List::Element *global_texture_E = nullptr; + uint64_t global_textures_pass = 0; + Map used_global_textures; }; typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *); @@ -555,6 +565,73 @@ private: void _update_render_target(RenderTarget *rt); void _create_render_target_backbuffer(RenderTarget *rt); + /* GLOBAL SHADER VARIABLES */ + + struct GlobalVariables { + + enum { + BUFFER_DIRTY_REGION_SIZE = 1024 + }; + struct Variable { + Set texture_materials; // materials using this + + RS::GlobalVariableType type; + Variant value; + Variant override; + int32_t buffer_index; //for vectors + int32_t buffer_elements; //for vectors + }; + + HashMap variables; + + struct Value { + float x; + float y; + float z; + float w; + }; + + struct ValueInt { + int32_t x; + int32_t y; + int32_t z; + int32_t w; + }; + + struct ValueUInt { + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t w; + }; + + struct ValueUsage { + uint32_t elements = 0; + }; + + List materials_using_buffer; + List materials_using_texture; + + RID buffer; + Value *buffer_values; + ValueUsage *buffer_usage; + bool *buffer_dirty_regions; + uint32_t buffer_dirty_region_count = 0; + + uint32_t buffer_size; + + bool must_update_texture_materials = false; + bool must_update_buffer_materials = false; + + HashMap instance_buffer_pos; + + } global_variables; + + int32_t _global_variable_allocate(uint32_t p_elements); + void _global_variable_store_in_buffer(int32_t p_index, RS::GlobalVariableType p_type, const Variant &p_value); + void _global_variable_mark_buffer_dirty(int32_t p_index, int32_t p_elements); + + void _update_global_variables(); /* EFFECTS */ RasterizerEffectsRD effects; @@ -675,6 +752,8 @@ public: bool material_is_animated(RID p_material); bool material_casts_shadows(RID p_material); + void material_get_instance_shader_parameters(RID p_material, List *r_parameters); + void material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance); void material_force_update_textures(RID p_material, ShaderType p_shader_type); @@ -1246,6 +1325,27 @@ public: virtual bool particles_is_inactive(RID p_particles) const { return false; } + /* GLOBAL VARIABLES API */ + + virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value); + virtual void global_variable_remove(const StringName &p_name); + virtual Vector global_variable_get_list() const; + + virtual void global_variable_set(const StringName &p_name, const Variant &p_value); + virtual void global_variable_set_override(const StringName &p_name, const Variant &p_value); + virtual Variant global_variable_get(const StringName &p_name) const; + virtual RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const; + RS::GlobalVariableType global_variable_get_type_internal(const StringName &p_name) const; + + virtual void global_variables_load_settings(bool p_load_textures = true); + virtual void global_variables_clear(); + + virtual int32_t global_variables_instance_allocate(RID p_instance); + virtual void global_variables_instance_free(RID p_instance); + virtual void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value); + + RID global_variables_get_storage_buffer() const; + /* RENDER TARGET API */ RID render_target_create(); @@ -1295,7 +1395,7 @@ public: virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const; virtual String get_captured_timestamp_name(uint32_t p_index) const; - static RasterizerStorage *base_singleton; + static RasterizerStorageRD *base_singleton; RasterizerEffectsRD *get_effects(); diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp index 4a0b4f02b1..9cbff2571a 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp @@ -32,6 +32,8 @@ #include "core/os/os.h" #include "core/project_settings.h" +#include "rasterizer_storage_rd.h" +#include "servers/rendering_server.h" #define SL ShaderLanguage @@ -91,6 +93,9 @@ static int _get_datatype_size(SL::DataType p_type) { case SL::TYPE_USAMPLER3D: return 16; case SL::TYPE_SAMPLERCUBE: return 16; case SL::TYPE_STRUCT: return 0; + case SL::TYPE_MAX: { + ERR_FAIL_V(0); + }; } ERR_FAIL_V(0); @@ -131,6 +136,9 @@ static int _get_datatype_alignment(SL::DataType p_type) { case SL::TYPE_USAMPLER3D: return 16; case SL::TYPE_SAMPLERCUBE: return 16; case SL::TYPE_STRUCT: return 0; + case SL::TYPE_MAX: { + ERR_FAIL_V(0); + } } ERR_FAIL_V(0); @@ -341,6 +349,71 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S } } +static String _get_global_variable_from_type_and_index(const String &p_buffer, const String &p_index, ShaderLanguage::DataType p_type) { + switch (p_type) { + case ShaderLanguage::TYPE_BOOL: { + return "(" + p_buffer + "[" + p_index + "].x != 0.0)"; + } + case ShaderLanguage::TYPE_BVEC2: { + return "(" + p_buffer + "[" + p_index + "].xy != vec2(0.0))"; + } + case ShaderLanguage::TYPE_BVEC3: { + return "(" + p_buffer + "[" + p_index + "].xyz != vec3(0.0))"; + } + case ShaderLanguage::TYPE_BVEC4: { + return "(" + p_buffer + "[" + p_index + "].xyzw != vec4(0.0))"; + } + case ShaderLanguage::TYPE_INT: { + return "floatBitsToInt(" + p_buffer + "[" + p_index + "].x)"; + } + case ShaderLanguage::TYPE_IVEC2: { + return "floatBitsToInt(" + p_buffer + "[" + p_index + "].xy)"; + } + case ShaderLanguage::TYPE_IVEC3: { + return "floatBitsToInt(" + p_buffer + "[" + p_index + "].xyz)"; + } + case ShaderLanguage::TYPE_IVEC4: { + return "floatBitsToInt(" + p_buffer + "[" + p_index + "].xyzw)"; + } + case ShaderLanguage::TYPE_UINT: { + return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].x)"; + } + case ShaderLanguage::TYPE_UVEC2: { + return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].xy)"; + } + case ShaderLanguage::TYPE_UVEC3: { + return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].xyz)"; + } + case ShaderLanguage::TYPE_UVEC4: { + return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].xyzw)"; + } + case ShaderLanguage::TYPE_FLOAT: { + return "(" + p_buffer + "[" + p_index + "].x)"; + } + case ShaderLanguage::TYPE_VEC2: { + return "(" + p_buffer + "[" + p_index + "].xy)"; + } + case ShaderLanguage::TYPE_VEC3: { + return "(" + p_buffer + "[" + p_index + "].xyz)"; + } + case ShaderLanguage::TYPE_VEC4: { + return "(" + p_buffer + "[" + p_index + "].xyzw)"; + } + case ShaderLanguage::TYPE_MAT2: { + return "mat2(" + p_buffer + "[" + p_index + "].xy," + p_buffer + "[" + p_index + "+1].xy)"; + } + case ShaderLanguage::TYPE_MAT3: { + return "mat3(" + p_buffer + "[" + p_index + "].xyz," + p_buffer + "[" + p_index + "+1].xyz," + p_buffer + "[" + p_index + "+2].xyz)"; + } + case ShaderLanguage::TYPE_MAT4: { + return "mat4(" + p_buffer + "[" + p_index + "].xyzw," + p_buffer + "[" + p_index + "+1].xyzw," + p_buffer + "[" + p_index + "+2].xyzw," + p_buffer + "[" + p_index + "+3].xyzw)"; + } + default: { + ERR_FAIL_V("void"); + } + } +} + String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning) { String code; @@ -408,10 +481,17 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge int max_uniforms = 0; for (Map::Element *E = pnode->uniforms.front(); E; E = E->next()) { - if (SL::is_sampler_type(E->get().type)) + + if (SL::is_sampler_type(E->get().type)) { max_texture_uniforms++; - else + } else { + + if (E->get().scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; //instances are indexed directly, dont need index uniforms + } + max_uniforms++; + } } r_gen_code.texture_uniforms.resize(max_texture_uniforms); @@ -428,12 +508,25 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge String ucode; + if (E->get().scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) { + //insert, but don't generate any code. + p_actions.uniforms->insert(E->key(), E->get()); + continue; //instances are indexed directly, dont need index uniforms + } if (SL::is_sampler_type(E->get().type)) { ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + E->get().texture_order) + ") uniform "; } - ucode += _prestr(E->get().precision); - ucode += _typestr(E->get().type); + bool is_buffer_global = !SL::is_sampler_type(E->get().type) && E->get().scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL; + + if (is_buffer_global) { + //this is an integer to index the global table + ucode += _typestr(ShaderLanguage::TYPE_UINT); + } else { + ucode += _prestr(E->get().precision); + ucode += _typestr(E->get().type); + } + ucode += " " + _mkid(E->key()); ucode += ";\n"; if (SL::is_sampler_type(E->get().type)) { @@ -446,6 +539,10 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge texture.type = E->get().type; texture.filter = E->get().filter; texture.repeat = E->get().repeat; + texture.global = E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL; + if (texture.global) { + r_gen_code.uses_global_textures = true; + } r_gen_code.texture_uniforms.write[E->get().texture_order] = texture; } else { @@ -455,8 +552,14 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge uses_uniforms = true; } uniform_defines.write[E->get().order] = ucode; - uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type); - uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type); + if (is_buffer_global) { + //globals are indices into the global table + uniform_sizes.write[E->get().order] = _get_datatype_size(ShaderLanguage::TYPE_UINT); + uniform_alignments.write[E->get().order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT); + } else { + uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type); + uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type); + } } p_actions.uniforms->insert(E->key(), E->get()); @@ -690,9 +793,29 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge if (p_default_actions.renames.has(vnode->name)) code = p_default_actions.renames[vnode->name]; else { - code = _mkid(vnode->name); - if (actions.base_uniform_string != String() && shader->uniforms.has(vnode->name) && shader->uniforms[vnode->name].texture_order < 0) { - code = actions.base_uniform_string + code; + if (shader->uniforms.has(vnode->name)) { + //its a uniform! + const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[vnode->name]; + if (u.texture_order >= 0) { + code = _mkid(vnode->name); //texture, use as is + } else { + //a scalar or vector + if (u.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) { + code = actions.base_uniform_string + _mkid(vnode->name); //texture, use as is + //global variable, this means the code points to an index to the global table + code = _get_global_variable_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type); + } else if (u.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + //instance variable, index it as such + code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + ")"; + code = _get_global_variable_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type); + } else { + //regular uniform, index from UBO + code = actions.base_uniform_string + _mkid(vnode->name); + } + } + + } else { + code = _mkid(vnode->name); //its something else (local var most likely) use as is } } @@ -1037,9 +1160,14 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge return code; } +ShaderLanguage::DataType ShaderCompilerRD::_get_variable_type(const StringName &p_type) { + RS::GlobalVariableType gvt = ((RasterizerStorageRD *)(RasterizerStorage::base_singleton))->global_variable_get_type_internal(p_type); + return RS::global_variable_type_get_shader_datatype(gvt); +} + Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code) { - Error err = parser.compile(p_code, ShaderTypes::get_singleton()->get_functions(p_mode), ShaderTypes::get_singleton()->get_modes(p_mode), ShaderTypes::get_singleton()->get_types()); + Error err = parser.compile(p_code, ShaderTypes::get_singleton()->get_functions(p_mode), ShaderTypes::get_singleton()->get_modes(p_mode), ShaderTypes::get_singleton()->get_types(), _get_variable_type); if (err != OK) { @@ -1060,6 +1188,7 @@ Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, Ide r_gen_code.light = String(); r_gen_code.uses_fragment_time = false; r_gen_code.uses_vertex_time = false; + r_gen_code.uses_global_textures = false; used_name_defines.clear(); used_rmode_defines.clear(); diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.h b/servers/rendering/rasterizer_rd/shader_compiler_rd.h index 7d78469e9c..16d53197a7 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.h +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.h @@ -57,6 +57,7 @@ public: ShaderLanguage::ShaderNode::Uniform::Hint hint; ShaderLanguage::TextureFilter filter; ShaderLanguage::TextureRepeat repeat; + bool global; }; Vector texture_uniforms; @@ -70,6 +71,7 @@ public: String fragment; String light; + bool uses_global_textures; bool uses_fragment_time; bool uses_vertex_time; }; @@ -86,6 +88,8 @@ public: int base_texture_binding_index = 0; int texture_layout_set = 0; String base_uniform_string; + String global_buffer_array_variable; + String instance_uniform_index_variable; uint32_t base_varying_index = 0; }; @@ -113,6 +117,8 @@ private: DefaultIdentifierActions actions; + static ShaderLanguage::DataType _get_variable_type(const StringName &p_type); + public: Error compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code); diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl index 1ac43480cd..a39866004b 100644 --- a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl @@ -132,6 +132,11 @@ layout(set = 2, binding = 6) uniform sampler shadow_sampler; #endif +layout(set = 2, binding = 7, std430) restrict readonly buffer GlobalVariableData { + vec4 data[]; +} +global_variables; + /* SET3: Render Target Data */ #ifdef SCREEN_TEXTURE_USED diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl index b5e3de5e82..ce4fabf9f2 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl @@ -134,7 +134,7 @@ struct InstanceData { mat4 transform; mat4 normal_transform; uint flags; - uint instance_ofs; //instance_offset in instancing/skeleton buffer + uint instance_uniforms_ofs; //base offset in global buffer for instance variables uint gi_offset; //GI information when using lightmapping (VCT or lightmap) uint layer_mask; }; @@ -287,6 +287,11 @@ cluster_data; layout(set = 0, binding = 15) uniform texture2D directional_shadow_atlas; +layout(set = 0, binding = 16, std430) restrict readonly buffer GlobalVariableData { + vec4 data[]; +} +global_variables; + // decal atlas /* Set 1, Radiance */ diff --git a/servers/rendering/rasterizer_rd/shaders/sky.glsl b/servers/rendering/rasterizer_rd/shaders/sky.glsl index e4019725cb..536077980d 100644 --- a/servers/rendering/rasterizer_rd/shaders/sky.glsl +++ b/servers/rendering/rasterizer_rd/shaders/sky.glsl @@ -58,6 +58,11 @@ params; layout(set = 0, binding = 0) uniform sampler material_samplers[12]; +layout(set = 0, binding = 1, std430) restrict readonly buffer GlobalVariableData { + vec4 data[]; +} +global_variables; + #ifdef USE_MATERIAL_UNIFORMS layout(set = 1, binding = 0, std140) uniform MaterialUniforms{ /* clang-format off */ diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 63aa8dde1c..97fe417def 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1025,7 +1025,6 @@ public: virtual uint32_t get_frame_delay() const = 0; static RenderingDevice *get_singleton(); - RenderingDevice(); }; diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 6f7ce7728d..f7b963a015 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -98,6 +98,8 @@ public: #define BIND0R(m_r, m_name) \ m_r m_name() { return BINDBASE->m_name(); } +#define BIND0RC(m_r, m_name) \ + m_r m_name() const { return BINDBASE->m_name(); } #define BIND1R(m_r, m_name, m_type1) \ m_r m_name(m_type1 arg1) { return BINDBASE->m_name(arg1); } #define BIND1RC(m_r, m_name, m_type1) \ @@ -111,8 +113,12 @@ public: #define BIND4RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \ m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) const { return BINDBASE->m_name(arg1, arg2, arg3, arg4); } +#define BIND0(m_name) \ + void m_name() { DISPLAY_CHANGED BINDBASE->m_name(); } #define BIND1(m_name, m_type1) \ void m_name(m_type1 arg1) { DISPLAY_CHANGED BINDBASE->m_name(arg1); } +#define BIND1C(m_name, m_type1) \ + void m_name(m_type1 arg1) const { DISPLAY_CHANGED BINDBASE->m_name(arg1); } #define BIND2(m_name, m_type1, m_type2) \ void m_name(m_type1 arg1, m_type2 arg2) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2); } #define BIND2C(m_name, m_type1, m_type2) \ @@ -620,6 +626,11 @@ public: BIND5(instance_geometry_set_draw_range, RID, float, float, float, float) BIND2(instance_geometry_set_as_instance_lod, RID, RID) + BIND3(instance_geometry_set_shader_parameter, RID, const StringName &, const Variant &) + BIND2RC(Variant, instance_geometry_get_shader_parameter, RID, const StringName &) + BIND2RC(Variant, instance_geometry_get_shader_parameter_default_value, RID, const StringName &) + BIND2C(instance_geometry_get_shader_parameter_list, RID, List *) + #undef BINDBASE //from now on, calls forwarded to this singleton #define BINDBASE RSG::canvas @@ -717,6 +728,23 @@ public: BIND2(canvas_occluder_polygon_set_cull_mode, RID, CanvasOccluderPolygonCullMode) + /* GLOBAL VARIABLES */ + +#undef BINDBASE +//from now on, calls forwarded to this singleton +#define BINDBASE RSG::storage + + BIND3(global_variable_add, const StringName &, GlobalVariableType, const Variant &) + BIND1(global_variable_remove, const StringName &) + BIND0RC(Vector, global_variable_get_list) + BIND2(global_variable_set, const StringName &, const Variant &) + BIND2(global_variable_set_override, const StringName &, const Variant &) + BIND1RC(GlobalVariableType, global_variable_get_type, const StringName &) + BIND1RC(Variant, global_variable_get, const StringName &) + + BIND1(global_variables_load_settings, bool) + BIND0(global_variables_clear) + /* BLACK BARS */ virtual void black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom); diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp index fc7c160c0b..9d141ea570 100644 --- a/servers/rendering/rendering_server_scene.cpp +++ b/servers/rendering/rendering_server_scene.cpp @@ -968,6 +968,67 @@ void RenderingServerScene::instance_geometry_set_draw_range(RID p_instance, floa void RenderingServerScene::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) { } +void RenderingServerScene::instance_geometry_set_shader_parameter(RID p_instance, const StringName &p_parameter, const Variant &p_value) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + Map::Element *E = instance->instance_shader_parameters.find(p_parameter); + + if (!E) { + RasterizerScene::InstanceBase::InstanceShaderParameter isp; + isp.index = -1; + isp.info = PropertyInfo(); + isp.value = p_value; + instance->instance_shader_parameters[p_parameter] = isp; + } else { + E->get().value = p_value; + if (E->get().index >= 0 && instance->instance_allocated_shader_parameters) { + //update directly + RSG::storage->global_variables_instance_update(p_instance, E->get().index, p_value); + } + } +} + +Variant RenderingServerScene::instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const { + + const Instance *instance = const_cast(this)->instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!instance, Variant()); + + if (instance->instance_shader_parameters.has(p_parameter)) { + return instance->instance_shader_parameters[p_parameter].value; + } + return Variant(); +} + +Variant RenderingServerScene::instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const { + + const Instance *instance = const_cast(this)->instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!instance, Variant()); + + if (instance->instance_shader_parameters.has(p_parameter)) { + return instance->instance_shader_parameters[p_parameter].default_value; + } + return Variant(); +} + +void RenderingServerScene::instance_geometry_get_shader_parameter_list(RID p_instance, List *p_parameters) const { + const Instance *instance = const_cast(this)->instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + const_cast(this)->update_dirty_instances(); + + Vector names; + for (Map::Element *E = instance->instance_shader_parameters.front(); E; E = E->next()) { + names.push_back(E->key()); + } + names.sort_custom(); + for (int i = 0; i < names.size(); i++) { + PropertyInfo pinfo = instance->instance_shader_parameters[names[i]].info; + p_parameters->push_back(pinfo); + } +} + void RenderingServerScene::_update_instance(Instance *p_instance) { p_instance->version++; @@ -2761,6 +2822,35 @@ void RenderingServerScene::render_probes() { } } +void RenderingServerScene::_update_instance_shader_parameters_from_material(Map &isparams, const Map &existing_isparams, RID p_material) { + + List plist; + RSG::storage->material_get_instance_shader_parameters(p_material, &plist); + for (List::Element *E = plist.front(); E; E = E->next()) { + StringName name = E->get().info.name; + if (isparams.has(name)) { + if (isparams[name].info.type != E->get().info.type) { + WARN_PRINT("More than one material in instance export the same instance shader uniform '" + E->get().info.name + "', but they do it with different data types. Only the first one (in order) will display correctly."); + } + if (isparams[name].index != E->get().index) { + WARN_PRINT("More than one material in instance export the same instance shader uniform '" + E->get().info.name + "', but they do it with different indices. Only the first one (in order) will display correctly."); + } + continue; //first one found always has priority + } + + RasterizerScene::InstanceBase::InstanceShaderParameter isp; + isp.index = E->get().index; + isp.info = E->get().info; + isp.default_value = E->get().default_value; + if (existing_isparams.has(name)) { + isp.value = existing_isparams[name].value; + } else { + isp.value = E->get().default_value; + } + isparams[name] = isp; + } +} + void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { if (p_instance->update_aabb) { @@ -2800,12 +2890,18 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { bool can_cast_shadows = true; bool is_animated = false; + Map isparams; if (p_instance->cast_shadows == RS::SHADOW_CASTING_SETTING_OFF) { can_cast_shadows = false; - } else if (p_instance->material_override.is_valid()) { - can_cast_shadows = RSG::storage->material_casts_shadows(p_instance->material_override); + } + + if (p_instance->material_override.is_valid()) { + if (!RSG::storage->material_casts_shadows(p_instance->material_override)) { + can_cast_shadows = false; + } is_animated = RSG::storage->material_is_animated(p_instance->material_override); + _update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, p_instance->material_override); } else { if (p_instance->base_type == RS::INSTANCE_MESH) { @@ -2830,6 +2926,8 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { is_animated = true; } + _update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat); + RSG::storage->material_update_dependency(mat, p_instance); } } @@ -2862,6 +2960,8 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { is_animated = true; } + _update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat); + RSG::storage->material_update_dependency(mat, p_instance); } } @@ -2876,12 +2976,18 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { RID mat = RSG::storage->immediate_get_material(p_instance->base); - can_cast_shadows = !mat.is_valid() || RSG::storage->material_casts_shadows(mat); + if (!(!mat.is_valid() || RSG::storage->material_casts_shadows(mat))) { + can_cast_shadows = false; + } if (mat.is_valid() && RSG::storage->material_is_animated(mat)) { is_animated = true; } + if (mat.is_valid()) { + _update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat); + } + if (mat.is_valid()) { RSG::storage->material_update_dependency(mat, p_instance); } @@ -2915,6 +3021,8 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { is_animated = true; } + _update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat); + RSG::storage->material_update_dependency(mat, p_instance); } } @@ -2937,6 +3045,22 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { } geom->material_is_animated = is_animated; + p_instance->instance_shader_parameters = isparams; + + if (p_instance->instance_allocated_shader_parameters != (p_instance->instance_shader_parameters.size() > 0)) { + p_instance->instance_allocated_shader_parameters = (p_instance->instance_shader_parameters.size() > 0); + if (p_instance->instance_allocated_shader_parameters) { + p_instance->instance_allocated_shader_parameters_offset = RSG::storage->global_variables_instance_allocate(p_instance->self); + for (Map::Element *E = p_instance->instance_shader_parameters.front(); E; E = E->next()) { + if (E->get().value.get_type() != Variant::NIL) { + RSG::storage->global_variables_instance_update(p_instance->self, E->get().index, E->get().value); + } + } + } else { + RSG::storage->global_variables_instance_free(p_instance->self); + p_instance->instance_allocated_shader_parameters_offset = -1; + } + } } if (p_instance->skeleton.is_valid()) { @@ -2998,6 +3122,10 @@ bool RenderingServerScene::free(RID p_rid) { instance_geometry_set_material_override(p_rid, RID()); instance_attach_skeleton(p_rid, RID()); + if (instance->instance_allocated_shader_parameters) { + //free the used shader parameters + RSG::storage->global_variables_instance_free(instance->self); + } update_dirty_instances(); //in case something changed this instance_owner.free(p_rid); diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index f5f7c50ea0..db2fbd6707 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -435,6 +435,13 @@ public: virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance); + void _update_instance_shader_parameters_from_material(Map &isparams, const Map &existing_isparams, RID p_material); + + virtual void instance_geometry_set_shader_parameter(RID p_instance, const StringName &p_parameter, const Variant &p_value); + virtual void instance_geometry_get_shader_parameter_list(RID p_instance, List *p_parameters) const; + virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const; + virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const; + _FORCE_INLINE_ void _update_instance(Instance *p_instance); _FORCE_INLINE_ void _update_instance_aabb(Instance *p_instance); _FORCE_INLINE_ void _update_dirty_instance(Instance *p_instance); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 79f328cb3b..d4e58485b8 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -532,6 +532,11 @@ public: FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float) FUNC2(instance_geometry_set_as_instance_lod, RID, RID) + FUNC3(instance_geometry_set_shader_parameter, RID, const StringName &, const Variant &) + FUNC2RC(Variant, instance_geometry_get_shader_parameter, RID, const StringName &) + FUNC2RC(Variant, instance_geometry_get_shader_parameter_default_value, RID, const StringName &) + FUNC2SC(instance_geometry_get_shader_parameter_list, RID, List *) + /* CANVAS (2D) */ FUNCRID(canvas) @@ -625,6 +630,18 @@ public: FUNC2(canvas_occluder_polygon_set_cull_mode, RID, CanvasOccluderPolygonCullMode) + /* GLOBAL VARIABLES */ + + FUNC3(global_variable_add, const StringName &, GlobalVariableType, const Variant &) + FUNC1(global_variable_remove, const StringName &) + FUNC0RC(Vector, global_variable_get_list) + FUNC2(global_variable_set, const StringName &, const Variant &) + FUNC2(global_variable_set_override, const StringName &, const Variant &) + FUNC1RC(GlobalVariableType, global_variable_get_type, const StringName &) + FUNC1RC(Variant, global_variable_get, const StringName &) + FUNC1(global_variables_load_settings, bool) + FUNC0(global_variables_clear) + /* BLACK BARS */ FUNC4(black_bars_set_margins, int, int, int, int) diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 76a81a4a1c..bec0958f71 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -194,6 +194,8 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "SEMICOLON", "PERIOD", "UNIFORM", + "INSTANCE", + "GLOBAL", "VARYING", "IN", "OUT", @@ -207,6 +209,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "HINT_BLACK_ALBEDO_TEXTURE", "HINT_COLOR", "HINT_RANGE", + "HINT_INSTANCE_INDEX", "FILTER_NEAREST", "FILTER_LINEAR", "FILTER_NEAREST_MIPMAP", @@ -300,6 +303,8 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_CF_RETURN, "return" }, { TK_CF_DISCARD, "discard" }, { TK_UNIFORM, "uniform" }, + { TK_INSTANCE, "instance" }, + { TK_GLOBAL, "global" }, { TK_VARYING, "varying" }, { TK_ARG_IN, "in" }, { TK_ARG_OUT, "out" }, @@ -319,6 +324,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_HINT_BLACK_ALBEDO_TEXTURE, "hint_black_albedo" }, { TK_HINT_COLOR, "hint_color" }, { TK_HINT_RANGE, "hint_range" }, + { TK_HINT_INSTANCE_INDEX, "instance_index" }, { TK_FILTER_NEAREST, "filter_nearest" }, { TK_FILTER_LINEAR, "filter_linear" }, { TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap" }, @@ -864,6 +870,7 @@ String ShaderLanguage::get_datatype_name(DataType p_type) { case TYPE_USAMPLER3D: return "usampler3D"; case TYPE_SAMPLERCUBE: return "samplerCube"; case TYPE_STRUCT: return "struct"; + case TYPE_MAX: return "invalid"; } return ""; @@ -2678,6 +2685,8 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector &p_funct int texture_uniforms = 0; int uniforms = 0; + int instance_index = 0; + ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; while (tk.type != TK_EOF) { @@ -5853,6 +5868,27 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct shader->vstructs.push_back(st); // struct's order is important! } break; + case TK_GLOBAL: { + + tk = _get_token(); + if (tk.type != TK_UNIFORM) { + _set_error("Expected 'uniform' after 'global'"); + return ERR_PARSE_ERROR; + } + uniform_scope = ShaderNode::Uniform::SCOPE_GLOBAL; + }; + [[fallthrough]]; + case TK_INSTANCE: { + if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) { + tk = _get_token(); + if (tk.type != TK_UNIFORM) { + _set_error("Expected 'uniform' after 'instance'"); + return ERR_PARSE_ERROR; + } + uniform_scope = ShaderNode::Uniform::SCOPE_INSTANCE; + } + }; + [[fallthrough]]; case TK_UNIFORM: case TK_VARYING: { @@ -5910,25 +5946,50 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct if (uniform) { + if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL) { + //validate global uniform + DataType gvtype = global_var_get_type_func(name); + if (gvtype == TYPE_MAX) { + _set_error("Global uniform '" + String(name) + "' does not exist. Create it in Project Settings."); + return ERR_PARSE_ERROR; + } + + if (type != gvtype) { + _set_error("Global uniform '" + String(name) + "' must be of type '" + get_datatype_name(gvtype) + "'."); + return ERR_PARSE_ERROR; + } + } ShaderNode::Uniform uniform2; if (is_sampler_type(type)) { + if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) { + _set_error("Uniforms with 'instance' qualifiers can't be of sampler type."); + return ERR_PARSE_ERROR; + } uniform2.texture_order = texture_uniforms++; uniform2.order = -1; if (_validate_datatype(type) != OK) { return ERR_PARSE_ERROR; } } else { + if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL && (type == TYPE_MAT2 || type == TYPE_MAT3 || type == TYPE_MAT4)) { + _set_error("Uniforms with 'instance' qualifiers can't be of matrix type."); + return ERR_PARSE_ERROR; + } + uniform2.texture_order = -1; uniform2.order = uniforms++; } uniform2.type = type; + uniform2.scope = uniform_scope; uniform2.precision = precision; //todo parse default value tk = _get_token(); + int custom_instance_index = -1; + if (tk.type == TK_COLON) { //hint do { @@ -6039,7 +6100,45 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct } if (tk.type != TK_PARENTHESIS_CLOSE) { - _set_error("Expected ','"); + _set_error("Expected ')'"); + return ERR_PARSE_ERROR; + } + } else if (tk.type == TK_HINT_INSTANCE_INDEX) { + + if (custom_instance_index != -1) { + _set_error("Can only specify 'instance_index' once."); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after 'instance_index'"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type == TK_OP_SUB) { + _set_error("The instance index can't be negative."); + return ERR_PARSE_ERROR; + } + + if (tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant"); + return ERR_PARSE_ERROR; + } + + custom_instance_index = tk.constant; + + if (custom_instance_index >= MAX_INSTANCE_UNIFORM_INDICES) { + _set_error("Allowed instance uniform indices are 0-" + itos(MAX_INSTANCE_UNIFORM_INDICES - 1)); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); return ERR_PARSE_ERROR; } } else if (tk.type == TK_FILTER_LINEAR) { @@ -6072,6 +6171,20 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct } while (tk.type == TK_COMMA); } + if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) { + if (custom_instance_index >= 0) { + uniform2.instance_index = custom_instance_index; + } else { + uniform2.instance_index = instance_index++; + if (instance_index > MAX_INSTANCE_UNIFORM_INDICES) { + _set_error("Too many 'instance' uniforms in shader, maximum supported is " + itos(MAX_INSTANCE_UNIFORM_INDICES)); + return ERR_PARSE_ERROR; + } + } + } + + //reset scope for next uniform + if (tk.type == TK_OP_ASSIGN) { Node *expr = _parse_and_reduce_expression(nullptr, Map()); @@ -6094,6 +6207,8 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct } shader->uniforms[name] = uniform2; + //reset scope for next uniform + uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; if (tk.type != TK_SEMICOLON) { _set_error("Expected ';'"); @@ -6639,11 +6754,12 @@ String ShaderLanguage::get_shader_type(const String &p_code) { return String(); } -Error ShaderLanguage::compile(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types) { +Error ShaderLanguage::compile(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) { clear(); code = p_code; + global_var_get_type_func = p_global_variable_type_func; nodes = nullptr; @@ -6656,13 +6772,14 @@ Error ShaderLanguage::compile(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types, List *r_options, String &r_call_hint) { +Error ShaderLanguage::complete(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List *r_options, String &r_call_hint) { clear(); code = p_code; nodes = nullptr; + global_var_get_type_func = p_global_variable_type_func; shader = alloc_node(); _parse_shader(p_functions, p_render_modes, p_shader_types); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index beabae0dda..48f1d1440f 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -143,6 +143,8 @@ public: TK_SEMICOLON, TK_PERIOD, TK_UNIFORM, + TK_INSTANCE, + TK_GLOBAL, TK_VARYING, TK_ARG_IN, TK_ARG_OUT, @@ -162,6 +164,7 @@ public: TK_HINT_BLACK_ALBEDO_TEXTURE, TK_HINT_COLOR, TK_HINT_RANGE, + TK_HINT_INSTANCE_INDEX, TK_FILTER_NEAREST, TK_FILTER_LINEAR, TK_FILTER_NEAREST_MIPMAP, @@ -216,6 +219,7 @@ public: TYPE_USAMPLER3D, TYPE_SAMPLERCUBE, TYPE_STRUCT, + TYPE_MAX }; enum DataPrecision { @@ -317,6 +321,10 @@ public: REPEAT_DEFAULT, }; + enum { + MAX_INSTANCE_UNIFORM_INDICES = 16 + }; + struct Node { Node *next; @@ -650,15 +658,23 @@ public: HINT_MAX }; + enum Scope { + SCOPE_LOCAL, + SCOPE_INSTANCE, + SCOPE_GLOBAL, + }; + int order; int texture_order; DataType type; DataPrecision precision; Vector default_value; + Scope scope; Hint hint; TextureFilter filter; TextureRepeat repeat; float hint_range[3]; + int instance_index; Uniform() : order(0), @@ -667,7 +683,8 @@ public: precision(PRECISION_DEFAULT), hint(HINT_NONE), filter(FILTER_DEFAULT), - repeat(REPEAT_DEFAULT) { + repeat(REPEAT_DEFAULT), + instance_index(0) { hint_range[0] = 0.0f; hint_range[1] = 1.0f; hint_range[2] = 0.001f; @@ -764,6 +781,8 @@ public: }; static bool has_builtin(const Map &p_functions, const StringName &p_name); + typedef DataType (*GlobalVariableGetTypeFunc)(const StringName &p_name); + private: struct KeyWord { TokenType token; @@ -772,6 +791,8 @@ private: static const KeyWord keyword_list[]; + GlobalVariableGetTypeFunc global_var_get_type_func; + bool error_set; String error_str; int error_line; @@ -884,8 +905,8 @@ public: void clear(); static String get_shader_type(const String &p_code); - Error compile(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types); - Error complete(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types, List *r_options, String &r_call_hint); + Error compile(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func); + Error complete(const String &p_code, const Map &p_functions, const Vector &p_render_modes, const Set &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List *r_options, String &r_call_hint); String get_error_text(); int get_error_line(); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index c288c2986e..0d3b44c0dc 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1565,6 +1565,42 @@ Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_su return arr; } #endif + +ShaderLanguage::DataType RenderingServer::global_variable_type_get_shader_datatype(GlobalVariableType p_type) { + + switch (p_type) { + case RS::GLOBAL_VAR_TYPE_BOOL: return ShaderLanguage::TYPE_BOOL; + case RS::GLOBAL_VAR_TYPE_BVEC2: return ShaderLanguage::TYPE_BVEC2; + case RS::GLOBAL_VAR_TYPE_BVEC3: return ShaderLanguage::TYPE_BVEC3; + case RS::GLOBAL_VAR_TYPE_BVEC4: return ShaderLanguage::TYPE_BVEC4; + case RS::GLOBAL_VAR_TYPE_INT: return ShaderLanguage::TYPE_INT; + case RS::GLOBAL_VAR_TYPE_IVEC2: return ShaderLanguage::TYPE_IVEC2; + case RS::GLOBAL_VAR_TYPE_IVEC3: return ShaderLanguage::TYPE_IVEC3; + case RS::GLOBAL_VAR_TYPE_IVEC4: return ShaderLanguage::TYPE_IVEC4; + case RS::GLOBAL_VAR_TYPE_RECT2I: return ShaderLanguage::TYPE_IVEC4; + case RS::GLOBAL_VAR_TYPE_UINT: return ShaderLanguage::TYPE_UINT; + case RS::GLOBAL_VAR_TYPE_UVEC2: return ShaderLanguage::TYPE_UVEC2; + case RS::GLOBAL_VAR_TYPE_UVEC3: return ShaderLanguage::TYPE_UVEC3; + case RS::GLOBAL_VAR_TYPE_UVEC4: return ShaderLanguage::TYPE_UVEC4; + case RS::GLOBAL_VAR_TYPE_FLOAT: return ShaderLanguage::TYPE_FLOAT; + case RS::GLOBAL_VAR_TYPE_VEC2: return ShaderLanguage::TYPE_VEC2; + case RS::GLOBAL_VAR_TYPE_VEC3: return ShaderLanguage::TYPE_VEC3; + case RS::GLOBAL_VAR_TYPE_VEC4: return ShaderLanguage::TYPE_VEC4; + case RS::GLOBAL_VAR_TYPE_COLOR: return ShaderLanguage::TYPE_VEC4; + case RS::GLOBAL_VAR_TYPE_RECT2: return ShaderLanguage::TYPE_VEC4; + case RS::GLOBAL_VAR_TYPE_MAT2: return ShaderLanguage::TYPE_MAT2; + case RS::GLOBAL_VAR_TYPE_MAT3: return ShaderLanguage::TYPE_MAT3; + case RS::GLOBAL_VAR_TYPE_MAT4: return ShaderLanguage::TYPE_MAT4; + case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: return ShaderLanguage::TYPE_MAT3; + case RS::GLOBAL_VAR_TYPE_TRANSFORM: return ShaderLanguage::TYPE_MAT4; + case RS::GLOBAL_VAR_TYPE_SAMPLER2D: return ShaderLanguage::TYPE_SAMPLER2D; + case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: return ShaderLanguage::TYPE_SAMPLER2DARRAY; + case RS::GLOBAL_VAR_TYPE_SAMPLER3D: return ShaderLanguage::TYPE_SAMPLER3D; + case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: return ShaderLanguage::TYPE_SAMPLERCUBE; + default: return ShaderLanguage::TYPE_MAX; //invalid or not found + } +} + void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("force_sync"), &RenderingServer::sync); @@ -1921,6 +1957,13 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_occluder_polygon_set_shape_as_lines", "occluder_polygon", "shape"), &RenderingServer::canvas_occluder_polygon_set_shape_as_lines); ClassDB::bind_method(D_METHOD("canvas_occluder_polygon_set_cull_mode", "occluder_polygon", "mode"), &RenderingServer::canvas_occluder_polygon_set_cull_mode); + ClassDB::bind_method(D_METHOD("global_variable_add", "name", "type", "default_value"), &RenderingServer::global_variable_add); + ClassDB::bind_method(D_METHOD("global_variable_remove", "name"), &RenderingServer::global_variable_remove); + ClassDB::bind_method(D_METHOD("global_variable_get_list"), &RenderingServer::global_variable_get_list); + ClassDB::bind_method(D_METHOD("global_variable_set", "name", "value"), &RenderingServer::global_variable_set); + ClassDB::bind_method(D_METHOD("global_variable_get", "name"), &RenderingServer::global_variable_get); + ClassDB::bind_method(D_METHOD("global_variable_get_type", "name"), &RenderingServer::global_variable_get_type); + ClassDB::bind_method(D_METHOD("black_bars_set_margins", "left", "top", "right", "bottom"), &RenderingServer::black_bars_set_margins); ClassDB::bind_method(D_METHOD("black_bars_set_images", "left", "top", "right", "bottom"), &RenderingServer::black_bars_set_images); @@ -2206,6 +2249,36 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE); BIND_ENUM_CONSTANT(CANVAS_OCCLUDER_POLYGON_CULL_COUNTER_CLOCKWISE); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_BOOL); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_BVEC2); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_BVEC3); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_BVEC4); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_INT); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_IVEC2); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_IVEC3); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_IVEC4); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_RECT2I); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_UINT); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_UVEC2); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_UVEC3); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_UVEC4); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_FLOAT); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_VEC2); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_VEC3); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_VEC4); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_COLOR); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_RECT2); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_MAT2); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_MAT3); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_MAT4); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_TRANSFORM_2D); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_TRANSFORM); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLER2D); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLER2DARRAY); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLER3D); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLERCUBE); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_MAX); + BIND_ENUM_CONSTANT(INFO_OBJECTS_IN_FRAME); BIND_ENUM_CONSTANT(INFO_VERTICES_IN_FRAME); BIND_ENUM_CONSTANT(INFO_MATERIAL_CHANGES_IN_FRAME); @@ -2370,6 +2443,8 @@ RenderingServer::RenderingServer() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/subsurface_scattering/subsurface_scattering_scale", PropertyInfo(Variant::FLOAT, "rendering/quality/subsurface_scattering/subsurface_scattering_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001")); GLOBAL_DEF("rendering/quality/subsurface_scattering/subsurface_scattering_depth_scale", 0.01); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/subsurface_scattering/subsurface_scattering_depth_scale", PropertyInfo(Variant::FLOAT, "rendering/quality/subsurface_scattering/subsurface_scattering_depth_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001")); + + GLOBAL_DEF("rendering/high_end/global_shader_variables_buffer_size", 65536); } RenderingServer::~RenderingServer() { diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 43c65d8007..3bc182a16b 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -38,6 +38,7 @@ #include "core/rid.h" #include "core/variant.h" #include "servers/display_server.h" +#include "servers/rendering/shader_language.h" class RenderingServer : public Object { @@ -950,6 +951,11 @@ public: virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0; + virtual void instance_geometry_set_shader_parameter(RID p_instance, const StringName &, const Variant &p_value) = 0; + virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &) const = 0; + virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &) const = 0; + virtual void instance_geometry_get_shader_parameter_list(RID p_instance, List *p_parameters) const = 0; + /* CANVAS (2D) */ virtual RID canvas_create() = 0; @@ -1090,6 +1096,55 @@ public: }; virtual void canvas_occluder_polygon_set_cull_mode(RID p_occluder_polygon, CanvasOccluderPolygonCullMode p_mode) = 0; + /* GLOBAL VARIABLES */ + + enum GlobalVariableType { + GLOBAL_VAR_TYPE_BOOL, + GLOBAL_VAR_TYPE_BVEC2, + GLOBAL_VAR_TYPE_BVEC3, + GLOBAL_VAR_TYPE_BVEC4, + GLOBAL_VAR_TYPE_INT, + GLOBAL_VAR_TYPE_IVEC2, + GLOBAL_VAR_TYPE_IVEC3, + GLOBAL_VAR_TYPE_IVEC4, + GLOBAL_VAR_TYPE_RECT2I, + GLOBAL_VAR_TYPE_UINT, + GLOBAL_VAR_TYPE_UVEC2, + GLOBAL_VAR_TYPE_UVEC3, + GLOBAL_VAR_TYPE_UVEC4, + GLOBAL_VAR_TYPE_FLOAT, + GLOBAL_VAR_TYPE_VEC2, + GLOBAL_VAR_TYPE_VEC3, + GLOBAL_VAR_TYPE_VEC4, + GLOBAL_VAR_TYPE_COLOR, + GLOBAL_VAR_TYPE_RECT2, + GLOBAL_VAR_TYPE_MAT2, + GLOBAL_VAR_TYPE_MAT3, + GLOBAL_VAR_TYPE_MAT4, + GLOBAL_VAR_TYPE_TRANSFORM_2D, + GLOBAL_VAR_TYPE_TRANSFORM, + GLOBAL_VAR_TYPE_SAMPLER2D, + GLOBAL_VAR_TYPE_SAMPLER2DARRAY, + GLOBAL_VAR_TYPE_SAMPLER3D, + GLOBAL_VAR_TYPE_SAMPLERCUBE, + GLOBAL_VAR_TYPE_MAX + }; + + virtual void global_variable_add(const StringName &p_name, GlobalVariableType p_type, const Variant &p_value) = 0; + virtual void global_variable_remove(const StringName &p_name) = 0; + virtual Vector global_variable_get_list() const = 0; + + virtual void global_variable_set(const StringName &p_name, const Variant &p_value) = 0; + virtual void global_variable_set_override(const StringName &p_name, const Variant &p_value) = 0; + + virtual Variant global_variable_get(const StringName &p_name) const = 0; + virtual GlobalVariableType global_variable_get_type(const StringName &p_name) const = 0; + + virtual void global_variables_load_settings(bool p_load_textures) = 0; + virtual void global_variables_clear() = 0; + + static ShaderLanguage::DataType global_variable_type_get_shader_datatype(GlobalVariableType p_type); + /* BLACK BARS */ virtual void black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom) = 0; @@ -1217,6 +1272,7 @@ VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureRepeat); VARIANT_ENUM_CAST(RenderingServer::CanvasLightMode); VARIANT_ENUM_CAST(RenderingServer::CanvasLightShadowFilter); VARIANT_ENUM_CAST(RenderingServer::CanvasOccluderPolygonCullMode); +VARIANT_ENUM_CAST(RenderingServer::GlobalVariableType); VARIANT_ENUM_CAST(RenderingServer::RenderInfo); VARIANT_ENUM_CAST(RenderingServer::Features);