diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 11359c4e62c..91c4320accb 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2900,7 +2900,22 @@ void CanvasShaderData::get_param_list(List *p_param_list) const { } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); @@ -3123,7 +3138,22 @@ void SkyShaderData::get_param_list(List *p_param_list) const { } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); @@ -3433,7 +3463,22 @@ void SceneShaderData::get_param_list(List *p_param_list) const { } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b7a3b677f5d..f07232a3ad7 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -202,7 +202,98 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { void ShaderMaterial::_get_property_list(List *p_list) const { if (!shader.is_null()) { - shader->get_param_list(p_list); + List list; + shader->get_param_list(&list, true); + + HashMap>> groups; + { + HashMap> none_subgroup; + none_subgroup.insert("", List()); + groups.insert("", none_subgroup); + } + + String last_group = ""; + String last_subgroup = ""; + + bool is_none_group_undefined = true; + bool is_none_group = true; + + for (List::Element *E = list.front(); E; E = E->next()) { + if (E->get().usage == PROPERTY_USAGE_GROUP) { + if (!E->get().name.is_empty()) { + Vector vgroup = E->get().name.split("::"); + last_group = vgroup[0]; + if (vgroup.size() > 1) { + last_subgroup = vgroup[1]; + } else { + last_subgroup = ""; + } + is_none_group = false; + + if (!groups.has(last_group)) { + PropertyInfo info; + info.usage = PROPERTY_USAGE_GROUP; + info.name = last_group; + + List none_subgroup; + none_subgroup.push_back(info); + + HashMap> subgroup_map; + subgroup_map.insert("", none_subgroup); + + groups.insert(last_group, subgroup_map); + } + + if (!groups[last_group].has(last_subgroup)) { + PropertyInfo info; + info.usage = PROPERTY_USAGE_SUBGROUP; + info.name = last_subgroup; + + List subgroup; + subgroup.push_back(info); + + groups[last_group].insert(last_subgroup, subgroup); + } + } else { + last_group = ""; + last_subgroup = ""; + is_none_group = true; + } + continue; // Pass group. + } + + if (is_none_group_undefined && is_none_group) { + is_none_group_undefined = false; + + PropertyInfo info; + info.usage = PROPERTY_USAGE_GROUP; + info.name = "Shader Param"; + groups[""][""].push_back(info); + } + + PropertyInfo info = E->get(); + info.name = info.name; + groups[last_group][last_subgroup].push_back(info); + } + + // Sort groups alphabetically. + List props; + for (HashMap>>::Iterator group = groups.begin(); group; ++group) { + for (HashMap>::Iterator subgroup = group->value.begin(); subgroup; ++subgroup) { + for (List::Element *item = subgroup->value.front(); item; item = item->next()) { + if (subgroup->key == "") { + props.push_back({ group->key, item->get() }); + } else { + props.push_back({ group->key + "::" + subgroup->key, item->get() }); + } + } + } + } + props.sort_custom(); + + for (List::Element *E = props.front(); E; E = E->next()) { + p_list->push_back(E->get().info); + } } } diff --git a/scene/resources/material.h b/scene/resources/material.h index 905e604e950..8c04817c6b2 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -84,6 +84,17 @@ class ShaderMaterial : public Material { HashMap param_cache; + struct UniformProp { + String str; + PropertyInfo info; + }; + + struct UniformPropComparator { + bool operator()(const UniformProp &p_a, const UniformProp &p_b) const { + return p_a.str.naturalnocasecmp_to(p_b.str) < 0; + } + }; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 16117986fe1..74031e02d7a 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -103,7 +103,7 @@ String Shader::get_code() const { return code; } -void Shader::get_param_list(List *p_params) const { +void Shader::get_param_list(List *p_params, bool p_get_groups) const { _update_shader(); List local; @@ -112,12 +112,16 @@ void Shader::get_param_list(List *p_params) const { params_cache_dirty = false; for (PropertyInfo &pi : local) { - if (default_textures.has(pi.name)) { //do not show default textures + bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP; + if (!p_get_groups && is_group) { continue; } - String original_name = pi.name; - pi.name = "shader_param/" + pi.name; - params_cache[pi.name] = original_name; + if (!is_group) { + if (default_textures.has(pi.name)) { //do not show default textures + continue; + } + params_cache[pi.name] = pi.name; + } if (p_params) { //small little hack if (pi.type == Variant::RID) { diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 5de8ad5518d..7aa14651a5a 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -78,7 +78,7 @@ public: void set_code(const String &p_code); String get_code() const; - void get_param_list(List *p_params) const; + void get_param_list(List *p_params, bool p_get_groups = false) const; bool has_param(const StringName &p_param) const; void set_default_texture_param(const StringName &p_param, const Ref &p_texture, int p_index = 0); diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 63c227e89b0..987c1dbb520 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -393,7 +393,22 @@ void Fog::FogShaderData::get_param_list(List *p_param_list) const } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 0851e754ea0..6f32c15d95a 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -178,8 +178,22 @@ void SkyRD::SkyShaderData::get_param_list(List *p_param_list) cons order[E.value.order] = E.key; } } - + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index a5592418469..a2995a8902d 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -407,7 +407,22 @@ void SceneShaderForwardClustered::ShaderData::get_param_list(List } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index afe4eac0b37..ee902e5c52c 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -364,7 +364,22 @@ void SceneShaderForwardMobile::ShaderData::get_param_list(List *p_ } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index cf749854e26..6e24920fc3c 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -2185,7 +2185,22 @@ void RendererCanvasRenderRD::CanvasShaderData::get_param_list(List } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index ea4bd6ab0be..75977c5bc9b 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1612,7 +1612,22 @@ void ParticlesStorage::ParticlesShaderData::get_param_list(List *p } } + String last_group; for (const KeyValue &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index cfabddded34..9fc3e8f8d90 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -310,6 +310,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { // global space keywords { TK_UNIFORM, "uniform", CF_GLOBAL_SPACE | CF_UNIFORM_KEYWORD, {}, {} }, + { TK_UNIFORM_GROUP, "group_uniforms", CF_GLOBAL_SPACE, {}, {} }, { TK_VARYING, "varying", CF_GLOBAL_SPACE, { "particles", "sky", "fog" }, {} }, { TK_CONST, "const", CF_BLOCK | CF_GLOBAL_SPACE | CF_CONST_KEYWORD, {}, {} }, { TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} }, @@ -1146,6 +1147,8 @@ void ShaderLanguage::clear() { current_function = StringName(); last_name = StringName(); last_type = IDENTIFIER_MAX; + current_uniform_group_name = ""; + current_uniform_subgroup_name = ""; completion_type = COMPLETION_NONE; completion_block = nullptr; @@ -8298,6 +8301,8 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f uniform.scope = uniform_scope; uniform.precision = precision; uniform.array_size = array_size; + uniform.group = current_uniform_group_name; + uniform.subgroup = current_uniform_subgroup_name; tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { @@ -8724,6 +8729,45 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f } } break; + case TK_UNIFORM_GROUP: { + tk = _get_token(); + if (tk.type == TK_IDENTIFIER) { + current_uniform_group_name = tk.text; + tk = _get_token(); + if (tk.type == TK_PERIOD) { + tk = _get_token(); + if (tk.type == TK_IDENTIFIER) { + current_uniform_subgroup_name = tk.text; + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_expected_error(";"); + return ERR_PARSE_ERROR; + } + } else { + _set_error(RTR("Expected an uniform subgroup identifier.")); + return ERR_PARSE_ERROR; + } + } else if (tk.type != TK_SEMICOLON) { + _set_expected_error(";", "."); + return ERR_PARSE_ERROR; + } + } else { + if (tk.type != TK_SEMICOLON) { + if (current_uniform_group_name.is_empty()) { + _set_error(RTR("Expected an uniform group identifier.")); + } else { + _set_error(RTR("Expected an uniform group identifier or `;`.")); + } + return ERR_PARSE_ERROR; + } else if (tk.type == TK_SEMICOLON && current_uniform_group_name.is_empty()) { + _set_error(RTR("Group needs to be opened before.")); + return ERR_PARSE_ERROR; + } else { + current_uniform_group_name = ""; + current_uniform_subgroup_name = ""; + } + } + } break; case TK_SHADER_TYPE: { _set_error(RTR("Shader type is already defined.")); return ERR_PARSE_ERROR; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 4a67e4d0886..42023f25d73 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -154,6 +154,7 @@ public: TK_SEMICOLON, TK_PERIOD, TK_UNIFORM, + TK_UNIFORM_GROUP, TK_INSTANCE, TK_GLOBAL, TK_VARYING, @@ -687,6 +688,8 @@ public: TextureRepeat repeat = REPEAT_DEFAULT; float hint_range[3]; int instance_index = 0; + String group; + String subgroup; Uniform() { hint_range[0] = 0.0f; @@ -938,6 +941,9 @@ private: StringName last_name; bool is_shader_inc = false; + String current_uniform_group_name; + String current_uniform_subgroup_name; + VaryingFunctionNames varying_function_names; TkPos _get_tkpos() {