diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 2a7383682fe4..71a6498a4219 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -363,6 +363,24 @@ The method for rendering the specular blob. See [enum SpecularMode]. [b]Note:[/b] [member specular_mode] only applies to the specular blob. It does not affect specular reflections from the sky, screen-space reflections, [VoxelGI], SDFGI or [ReflectionProbe]s. To disable reflections from these sources as well, set [member metallic_specular] to [code]0.0[/code] instead. + + The primary color of the stencil effect. + + + The comparison operator to use for stencil masking operations. See [enum StencilCompare]. + + + The flags dictating how the stencil operation behaves. See [enum StencilFlags]. + + + The stencil effect mode. See [enum StencilMode]. + + + The outline thickness for STENCIL_MODE_OUTLINE. + + + The stencil reference value (0-255). Typically a power of 2. + If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. Subsurface scattering quality is controlled by [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_quality]. @@ -805,5 +823,47 @@ Smoothly fades the object out based on the object's distance from the camera using a dithering approach. Dithering discards pixels based on a set pattern to smoothly fade without enabling transparency. On certain hardware, this can be faster than [constant DISTANCE_FADE_PIXEL_ALPHA] and [constant DISTANCE_FADE_PIXEL_DITHER]. + + Disables stencil operations. + + + Stencil preset which applies an outline to the object. Requires a next_pass. + + + Stencil preset which shows a silhouette of the object behind walls. Requires a next_pass. + + + Enables stencil operations without a preset. + + + The material will only be rendered where it passes a stencil comparison with existing stencil buffer values. See [enum StencilCompare]. + + + The material will write the reference value to the stencil buffer where it passes the depth test. + + + The material will write the reference value to the stencil buffer where it fails the depth test. + + + Passes the stencil test when the reference value is less than the existing stencil value. + + + Passes the stencil test when the reference value is equal to the existing stencil value. + + + Passes the stencil test when the reference value is less than or equal to the existing stencil value. + + + Passes the stencil test when the reference value is greater than the existing stencil value. + + + Passes the stencil test when the reference value is not equal to the existing stencil value. + + + Passes the stencil test when the reference value is greater than or equal to the existing stencil value. + + + Always passes the stencil test. + diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 276c57533f72..82e2d2c7d717 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -268,17 +268,35 @@ void ShaderTextEditor::_load_theme_settings() { } } - const Vector &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + { + const Vector &render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + + for (int j = 0; j < render_modes.size(); j++) { + const ShaderLanguage::ModeInfo &mode_info = render_modes[j]; + + if (!mode_info.options.is_empty()) { + for (int k = 0; k < mode_info.options.size(); k++) { + built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[k])); + } + } else { + built_ins.push_back(String(mode_info.name)); + } + } + } - for (int j = 0; j < modes.size(); j++) { - const ShaderLanguage::ModeInfo &mode_info = modes[j]; + { + const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)); - if (!mode_info.options.is_empty()) { - for (int k = 0; k < mode_info.options.size(); k++) { - built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[k])); + for (int j = 0; j < stencil_modes.size(); j++) { + const ShaderLanguage::ModeInfo &mode_info = stencil_modes[j]; + + if (!mode_info.options.is_empty()) { + for (int k = 0; k < mode_info.options.size(); k++) { + built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[k])); + } + } else { + built_ins.push_back(String(mode_info.name)); } - } else { - built_ins.push_back(String(mode_info.name)); } } } @@ -289,17 +307,35 @@ void ShaderTextEditor::_load_theme_settings() { } } - const Vector &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + { + const Vector &shader_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + + for (int i = 0; i < shader_modes.size(); i++) { + const ShaderLanguage::ModeInfo &mode_info = shader_modes[i]; + + if (!mode_info.options.is_empty()) { + for (int j = 0; j < mode_info.options.size(); j++) { + built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[j])); + } + } else { + built_ins.push_back(String(mode_info.name)); + } + } + } + + { + const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode())); - for (int i = 0; i < modes.size(); i++) { - const ShaderLanguage::ModeInfo &mode_info = modes[i]; + for (int i = 0; i < stencil_modes.size(); i++) { + const ShaderLanguage::ModeInfo &mode_info = stencil_modes[i]; - if (!mode_info.options.is_empty()) { - for (int j = 0; j < mode_info.options.size(); j++) { - built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[j])); + if (!mode_info.options.is_empty()) { + for (int j = 0; j < mode_info.options.size(); j++) { + built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[j])); + } + } else { + built_ins.push_back(String(mode_info.name)); } - } else { - built_ins.push_back(String(mode_info.name)); } } } @@ -436,6 +472,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, Listget_functions(RenderingServer::ShaderMode(shader->get_mode())); comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode())); comp_info.shader_types = ShaderTypes::get_singleton()->get_types(); sl.complete(code, comp_info, r_options, calltip); @@ -540,6 +577,7 @@ void ShaderTextEditor::_validate_script() { Shader::Mode mode = shader->get_mode(); comp_info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode)); comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode)); + comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(mode)); comp_info.shader_types = ShaderTypes::get_singleton()->get_types(); } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 3215f85293a8..62f57fa31921 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5942,6 +5942,7 @@ void VisualShaderEditor::_update_preview() { ShaderLanguage::ShaderCompileInfo info; info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())); info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())); + info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(visual_shader->get_mode())); info.shader_types = ShaderTypes::get_singleton()->get_types(); info.global_shader_uniform_type_func = _visual_shader_editor_get_global_shader_uniform_type; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index ea121003b69b..85e365a01985 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -845,6 +845,50 @@ void BaseMaterial3D::_update_shader() { code += ";\n"; + if (stencil_mode != STENCIL_MODE_DISABLED) { + code += "stencil_mode "; + + if (stencil_flags & STENCIL_FLAG_READ) { + code += "read,"; + } + + if (stencil_flags & STENCIL_FLAG_WRITE) { + code += "write,"; + } + + if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) { + code += "write_depth_fail,"; + } + + switch (stencil_compare) { + case STENCIL_COMPARE_LESS: + code += "compare_less,"; + break; + case STENCIL_COMPARE_EQUAL: + code += "compare_equal,"; + break; + case STENCIL_COMPARE_LESS_OR_EQUAL: + code += "compare_less_or_equal,"; + break; + case STENCIL_COMPARE_GREATER: + code += "compare_greater,"; + break; + case STENCIL_COMPARE_NOT_EQUAL: + code += "compare_not_equal,"; + break; + case STENCIL_COMPARE_GREATER_OR_EQUAL: + code += "compare_greater_or_equal,"; + break; + case STENCIL_COMPARE_ALWAYS: + code += "compare_always,"; + break; + case STENCIL_COMPARE_MAX: + break; + } + + code += vformat("%s;\n", stencil_reference); + } + // Generate list of uniforms. code += vformat(R"( uniform vec4 albedo : source_color; @@ -2433,6 +2477,22 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NONE; } + if (p_property.name == "stencil_reference" && stencil_mode == STENCIL_MODE_DISABLED) { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if ((p_property.name == "stencil_flags" || p_property.name == "stencil_compare") && stencil_mode != STENCIL_MODE_CUSTOM) { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if (p_property.name == "stencil_color" && stencil_mode != STENCIL_MODE_OUTLINE && stencil_mode != STENCIL_MODE_XRAY) { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if (p_property.name == "stencil_outline_thickness" && stencil_mode != STENCIL_MODE_OUTLINE) { + p_property.usage = PROPERTY_USAGE_NONE; + } + if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (p_property.name == "subsurf_scatter_transmittance_color" || p_property.name == "subsurf_scatter_transmittance_texture")) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -2877,6 +2937,171 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const { return emission_op; } +void BaseMaterial3D::_prepare_stencil_effect() { + const Ref current_next_pass = get_next_pass(); + + if (stencil_mode == STENCIL_MODE_DISABLED || stencil_mode == STENCIL_MODE_CUSTOM) { + if (current_next_pass.is_valid() && current_next_pass->has_meta("_stencil_owned")) { + set_next_pass(current_next_pass->get_next_pass()); + } + return; + } + + Ref stencil_next_pass; + + if (!current_next_pass.is_valid() || !current_next_pass->has_meta("_stencil_owned")) { + stencil_next_pass = Ref(memnew(StandardMaterial3D)); + stencil_next_pass->set_meta("_stencil_owned", true); + stencil_next_pass->set_next_pass(current_next_pass); + set_next_pass(stencil_next_pass); + } else { + stencil_next_pass = current_next_pass; + } + + switch (stencil_mode) { + case STENCIL_MODE_DISABLED: + break; + case STENCIL_MODE_OUTLINE: + set_stencil_flags(STENCIL_FLAG_WRITE); + set_stencil_compare(STENCIL_COMPARE_ALWAYS); + stencil_next_pass->set_render_priority(-1); + stencil_next_pass->set_shading_mode(SHADING_MODE_UNSHADED); + stencil_next_pass->set_transparency(TRANSPARENCY_ALPHA); + stencil_next_pass->set_flag(FLAG_DISABLE_DEPTH_TEST, false); + stencil_next_pass->set_grow_enabled(true); + stencil_next_pass->set_grow(stencil_effect_outline_thickness); + stencil_next_pass->set_albedo(stencil_effect_color); + stencil_next_pass->set_stencil_mode(STENCIL_MODE_CUSTOM); + stencil_next_pass->set_stencil_flags(STENCIL_FLAG_READ | STENCIL_FLAG_WRITE); + stencil_next_pass->set_stencil_compare(STENCIL_COMPARE_NOT_EQUAL); + stencil_next_pass->set_stencil_reference(stencil_reference); + break; + case STENCIL_MODE_XRAY: + set_stencil_flags(STENCIL_FLAG_WRITE); + set_stencil_compare(STENCIL_COMPARE_ALWAYS); + stencil_next_pass->set_render_priority(-1); + stencil_next_pass->set_shading_mode(SHADING_MODE_UNSHADED); + stencil_next_pass->set_transparency(TRANSPARENCY_ALPHA); + stencil_next_pass->set_flag(FLAG_DISABLE_DEPTH_TEST, true); + stencil_next_pass->set_grow_enabled(false); + stencil_next_pass->set_grow(0); + stencil_next_pass->set_albedo(stencil_effect_color); + stencil_next_pass->set_stencil_mode(STENCIL_MODE_CUSTOM); + stencil_next_pass->set_stencil_flags(STENCIL_FLAG_READ | STENCIL_FLAG_WRITE); + stencil_next_pass->set_stencil_compare(STENCIL_COMPARE_NOT_EQUAL); + stencil_next_pass->set_stencil_reference(stencil_reference); + break; + case STENCIL_MODE_CUSTOM: + break; + case STENCIL_MODE_MAX: + break; + } +} + +Ref BaseMaterial3D::_get_stencil_next_pass() const { + const Ref current_next_pass = get_next_pass(); + Ref stencil_next_pass; + + if (current_next_pass.is_valid() && current_next_pass->has_meta("_stencil_owned")) { + stencil_next_pass = current_next_pass; + } + + return stencil_next_pass; +} + +void BaseMaterial3D::set_stencil_mode(StencilMode p_stencil_mode) { + if (stencil_mode == p_stencil_mode) { + return; + } + + stencil_mode = p_stencil_mode; + _prepare_stencil_effect(); + _queue_shader_change(); + notify_property_list_changed(); +} + +BaseMaterial3D::StencilMode BaseMaterial3D::get_stencil_mode() const { + return stencil_mode; +} + +void BaseMaterial3D::set_stencil_flags(int p_stencil_flags) { + if (stencil_flags == p_stencil_flags) { + return; + } + + stencil_flags = p_stencil_flags; + _queue_shader_change(); +} + +int BaseMaterial3D::get_stencil_flags() const { + return stencil_flags; +} + +void BaseMaterial3D::set_stencil_compare(BaseMaterial3D::StencilCompare p_op) { + if (stencil_compare == p_op) { + return; + } + + stencil_compare = p_op; + _queue_shader_change(); +} + +BaseMaterial3D::StencilCompare BaseMaterial3D::get_stencil_compare() const { + return stencil_compare; +} + +void BaseMaterial3D::set_stencil_reference(int p_reference) { + if (stencil_reference == p_reference) { + return; + } + + stencil_reference = p_reference; + _queue_shader_change(); + + Ref stencil_next_pass = _get_stencil_next_pass(); + if (stencil_next_pass.is_valid()) { + stencil_next_pass->set_stencil_reference(p_reference); + } +} + +int BaseMaterial3D::get_stencil_reference() const { + return stencil_reference; +} + +void BaseMaterial3D::set_stencil_effect_color(const Color &p_color) { + if (stencil_effect_color == p_color) { + return; + } + + stencil_effect_color = p_color; + + Ref stencil_next_pass = _get_stencil_next_pass(); + if (stencil_next_pass.is_valid()) { + stencil_next_pass->set_albedo(p_color); + } +} + +Color BaseMaterial3D::get_stencil_effect_color() const { + return stencil_effect_color; +} + +void BaseMaterial3D::set_stencil_effect_outline_thickness(float p_outline_thickness) { + if (stencil_effect_outline_thickness == p_outline_thickness) { + return; + } + + stencil_effect_outline_thickness = p_outline_thickness; + + Ref stencil_next_pass = _get_stencil_next_pass(); + if (stencil_next_pass.is_valid()) { + stencil_next_pass->set_grow(p_outline_thickness); + } +} + +float BaseMaterial3D::get_stencil_effect_outline_thickness() const { + return stencil_effect_outline_thickness; +} + RID BaseMaterial3D::get_shader_rid() const { MutexLock lock(material_mutex); if (element.in_list()) { @@ -3100,6 +3325,24 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_distance_fade_min_distance", "distance"), &BaseMaterial3D::set_distance_fade_min_distance); ClassDB::bind_method(D_METHOD("get_distance_fade_min_distance"), &BaseMaterial3D::get_distance_fade_min_distance); + ClassDB::bind_method(D_METHOD("set_stencil_mode", "stencil_mode"), &BaseMaterial3D::set_stencil_mode); + ClassDB::bind_method(D_METHOD("get_stencil_mode"), &BaseMaterial3D::get_stencil_mode); + + ClassDB::bind_method(D_METHOD("set_stencil_flags", "stencil_flags"), &BaseMaterial3D::set_stencil_flags); + ClassDB::bind_method(D_METHOD("get_stencil_flags"), &BaseMaterial3D::get_stencil_flags); + + ClassDB::bind_method(D_METHOD("set_stencil_compare", "stencil_compare"), &BaseMaterial3D::set_stencil_compare); + ClassDB::bind_method(D_METHOD("get_stencil_compare"), &BaseMaterial3D::get_stencil_compare); + + ClassDB::bind_method(D_METHOD("set_stencil_reference", "stencil_reference"), &BaseMaterial3D::set_stencil_reference); + ClassDB::bind_method(D_METHOD("get_stencil_reference"), &BaseMaterial3D::get_stencil_reference); + + ClassDB::bind_method(D_METHOD("set_stencil_effect_color", "stencil_color"), &BaseMaterial3D::set_stencil_effect_color); + ClassDB::bind_method(D_METHOD("get_stencil_effect_color"), &BaseMaterial3D::get_stencil_effect_color); + + ClassDB::bind_method(D_METHOD("set_stencil_effect_outline_thickness", "stencil_outline_thickness"), &BaseMaterial3D::set_stencil_effect_outline_thickness); + ClassDB::bind_method(D_METHOD("get_stencil_effect_outline_thickness"), &BaseMaterial3D::get_stencil_effect_outline_thickness); + ADD_GROUP("Transparency", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); @@ -3275,6 +3518,15 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_min_distance", "get_distance_fade_min_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_max_distance", "get_distance_fade_max_distance"); + ADD_GROUP("Stencil", "stencil_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_mode", PROPERTY_HINT_ENUM, "Disabled,Outline,Xray,Custom"), "set_stencil_mode", "get_stencil_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_flags", PROPERTY_HINT_FLAGS, "Read,Write,Write Depth Fail"), "set_stencil_flags", "get_stencil_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_compare", PROPERTY_HINT_ENUM, "Less,Equal,Less Or Equal,Greater,Not Equal,Greater Or Equal,Always"), "set_stencil_compare", "get_stencil_compare"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_reference", PROPERTY_HINT_RANGE, "0,255,1"), "set_stencil_reference", "get_stencil_reference"); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "stencil_color", PROPERTY_HINT_NONE), "set_stencil_effect_color", "get_stencil_effect_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stencil_outline_thickness", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_stencil_effect_outline_thickness", "get_stencil_effect_outline_thickness"); + BIND_ENUM_CONSTANT(TEXTURE_ALBEDO); BIND_ENUM_CONSTANT(TEXTURE_METALLIC); BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS); @@ -3410,6 +3662,23 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_ALPHA); BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_DITHER); BIND_ENUM_CONSTANT(DISTANCE_FADE_OBJECT_DITHER); + + BIND_ENUM_CONSTANT(STENCIL_MODE_DISABLED); + BIND_ENUM_CONSTANT(STENCIL_MODE_OUTLINE); + BIND_ENUM_CONSTANT(STENCIL_MODE_XRAY); + BIND_ENUM_CONSTANT(STENCIL_MODE_CUSTOM); + + BIND_ENUM_CONSTANT(STENCIL_FLAG_READ); + BIND_ENUM_CONSTANT(STENCIL_FLAG_WRITE); + BIND_ENUM_CONSTANT(STENCIL_FLAG_WRITE_DEPTH_FAIL); + + BIND_ENUM_CONSTANT(STENCIL_COMPARE_LESS); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_LESS_OR_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_GREATER); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_NOT_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_GREATER_OR_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_ALWAYS); } BaseMaterial3D::BaseMaterial3D(bool p_orm) : @@ -3475,6 +3744,8 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_heightmap_deep_parallax_max_layers(32); set_heightmap_deep_parallax_flip_tangent(false); //also sets binormal + set_stencil_mode(STENCIL_MODE_DISABLED); + flags[FLAG_ALBEDO_TEXTURE_MSDF] = false; flags[FLAG_USE_TEXTURE_REPEAT] = true; diff --git a/scene/resources/material.h b/scene/resources/material.h index 77288734691f..7682a334e67f 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -322,6 +322,33 @@ class BaseMaterial3D : public Material { DISTANCE_FADE_MAX }; + enum StencilMode { + STENCIL_MODE_DISABLED, + STENCIL_MODE_OUTLINE, + STENCIL_MODE_XRAY, + STENCIL_MODE_CUSTOM, + STENCIL_MODE_MAX // Not an actual mode, just the amount of modes. + }; + + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + + STENCIL_FLAG_NUM_BITS = 3 // Not an actual mode, just the amount of bits. + }; + + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators. + }; + private: struct MaterialKey { // enum values @@ -341,6 +368,13 @@ class BaseMaterial3D : public Material { uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1); uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1); uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1); + + // stencil + uint64_t stencil_mode : get_num_bits(STENCIL_MODE_MAX - 1); + uint64_t stencil_flags : STENCIL_FLAG_NUM_BITS; + uint64_t stencil_compare : get_num_bits(STENCIL_COMPARE_MAX - 1); + uint64_t stencil_reference : 8; + // booleans uint64_t invalid_key : 1; uint64_t deep_parallax : 1; @@ -401,6 +435,11 @@ class BaseMaterial3D : public Material { mk.alpha_antialiasing_mode = alpha_antialiasing_mode; mk.orm = orm; + mk.stencil_mode = stencil_mode; + mk.stencil_flags = stencil_flags; + mk.stencil_compare = stencil_compare; + mk.stencil_reference = stencil_reference; + for (int i = 0; i < FEATURE_MAX; i++) { if (features[i]) { mk.feature_mask |= ((uint64_t)1 << i); @@ -565,12 +604,23 @@ class BaseMaterial3D : public Material { AlphaAntiAliasing alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF; + StencilMode stencil_mode = STENCIL_MODE_DISABLED; + int stencil_flags = STENCIL_FLAG_READ; + StencilCompare stencil_compare = STENCIL_COMPARE_EQUAL; + int stencil_reference = 1; + + Color stencil_effect_color; + float stencil_effect_outline_thickness = 0.01f; + bool features[FEATURE_MAX] = {}; Ref textures[TEXTURE_MAX]; _FORCE_INLINE_ void _validate_feature(const String &text, Feature feature, PropertyInfo &property) const; + void _prepare_stencil_effect(); + Ref _get_stencil_next_pass() const; + static HashMap> materials_for_2d; //used by Sprite3D, Label3D and other stuff protected: @@ -778,6 +828,24 @@ class BaseMaterial3D : public Material { void set_emission_operator(EmissionOperator p_op); EmissionOperator get_emission_operator() const; + void set_stencil_mode(StencilMode p_stencil_mode); + StencilMode get_stencil_mode() const; + + void set_stencil_flags(int p_stencil_flags); + int get_stencil_flags() const; + + void set_stencil_compare(StencilCompare p_op); + StencilCompare get_stencil_compare() const; + + void set_stencil_reference(int p_reference); + int get_stencil_reference() const; + + void set_stencil_effect_color(const Color &p_color); + Color get_stencil_effect_color() const; + + void set_stencil_effect_outline_thickness(float p_outline_thickness); + float get_stencil_effect_outline_thickness() const; + void set_metallic_texture_channel(TextureChannel p_channel); TextureChannel get_metallic_texture_channel() const; void set_roughness_texture_channel(TextureChannel p_channel); @@ -819,6 +887,9 @@ VARIANT_ENUM_CAST(BaseMaterial3D::BillboardMode) VARIANT_ENUM_CAST(BaseMaterial3D::TextureChannel) VARIANT_ENUM_CAST(BaseMaterial3D::EmissionOperator) VARIANT_ENUM_CAST(BaseMaterial3D::DistanceFadeMode) +VARIANT_ENUM_CAST(BaseMaterial3D::StencilMode) +VARIANT_ENUM_CAST(BaseMaterial3D::StencilFlags) +VARIANT_ENUM_CAST(BaseMaterial3D::StencilCompare) class StandardMaterial3D : public BaseMaterial3D { GDCLASS(StandardMaterial3D, BaseMaterial3D) diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4bedcb18200f..bb5fc605214d 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1686,6 +1686,35 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { } _queue_update(); return true; + } else if (prop_name == "stencil/enabled") { + stencil_enabled = bool(p_value); + _queue_update(); + notify_property_list_changed(); + return true; + } else if (prop_name == "stencil/reference") { + stencil_reference = int(p_value); + _queue_update(); + return true; + } else if (prop_name.begins_with("stencil_flags/")) { + StringName flag = prop_name.get_slicec('/', 1); + bool enable = p_value; + if (enable) { + stencil_flags.insert(flag); + } else { + stencil_flags.erase(flag); + } + _queue_update(); + return true; + } else if (prop_name.begins_with("stencil_modes/")) { + String mode_name = prop_name.get_slicec('/', 1); + int value = p_value; + if (value == 0) { + stencil_modes.erase(mode_name); // It's default anyway, so don't store it. + } else { + stencil_modes[mode_name] = value; + } + _queue_update(); + return true; } else if (prop_name.begins_with("varyings/")) { String var_name = prop_name.get_slicec('/', 1); Varying value = Varying(); @@ -1760,6 +1789,24 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { r_ret = 0; } return true; + } else if (prop_name == "stencil/enabled") { + r_ret = stencil_enabled; + return true; + } else if (prop_name == "stencil/reference") { + r_ret = stencil_reference; + return true; + } else if (prop_name.begins_with("stencil_flags/")) { + StringName flag = prop_name.get_slicec('/', 1); + r_ret = stencil_flags.has(flag); + return true; + } else if (prop_name.begins_with("stencil_modes/")) { + String mode_name = prop_name.get_slicec('/', 1); + if (stencil_modes.has(mode_name)) { + r_ret = stencil_modes[mode_name]; + } else { + r_ret = 0; + } + return true; } else if (prop_name.begins_with("varyings/")) { String var_name = prop_name.get_slicec('/', 1); if (varyings.has(var_name)) { @@ -1861,6 +1908,47 @@ void VisualShader::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("flags"), E))); } + const Vector &smodes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader_mode)); + + if (smodes.size() > 0) { + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("stencil"), PNAME("enabled")))); + + uint32_t stencil_prop_usage = stencil_enabled ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE; + + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("stencil"), PNAME("reference")), PROPERTY_HINT_RANGE, "0,255,1", stencil_prop_usage)); + + HashMap stencil_enums; + HashSet stencil_toggles; + + for (int i = 0; i < smodes.size(); i++) { + const ShaderLanguage::ModeInfo &info = smodes[i]; + + if (!info.options.is_empty()) { + const String begin = String(info.name); + + for (int j = 0; j < info.options.size(); j++) { + const String option = String(info.options[j]).capitalize(); + + if (!stencil_enums.has(begin)) { + stencil_enums[begin] = option; + } else { + stencil_enums[begin] += "," + option; + } + } + } else { + stencil_toggles.insert(String(info.name)); + } + } + + for (const KeyValue &E : stencil_enums) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("stencil_modes"), E.key), PROPERTY_HINT_ENUM, E.value, stencil_prop_usage)); + } + + for (const String &E : stencil_toggles) { + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("stencil_flags"), E), PROPERTY_HINT_NONE, "", stencil_prop_usage)); + } + } + for (const KeyValue &E : varyings) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } @@ -2523,6 +2611,47 @@ void VisualShader::_update_shader() const { global_code += "render_mode " + render_mode + ";\n\n"; } + const Vector &smodes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader_mode)); + + if (smodes.size() > 0 && stencil_enabled) { + String stencil_mode; + + Vector flag_names; + + // Add enum modes first. + for (int i = 0; i < smodes.size(); i++) { + const ShaderLanguage::ModeInfo &info = smodes[i]; + const String temp = String(info.name); + + if (!info.options.is_empty()) { + if (stencil_modes.has(temp) && stencil_modes[temp] < info.options.size()) { + if (!stencil_mode.is_empty()) { + stencil_mode += ", "; + } + stencil_mode += temp + "_" + info.options[stencil_modes[temp]]; + } + } else if (stencil_flags.has(temp)) { + flag_names.push_back(temp); + } + } + + // Add flags afterward. + for (int i = 0; i < flag_names.size(); i++) { + if (!stencil_mode.is_empty()) { + stencil_mode += ", "; + } + stencil_mode += flag_names[i]; + } + + // Add reference value. + if (!stencil_mode.is_empty()) { + stencil_mode += ", "; + } + stencil_mode += itos(stencil_reference); + + global_code += "stencil_mode " + stencil_mode + ";\n\n"; + } + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; String global_expressions; diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 9cd8f86d0fe3..85fe439d766c 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -141,6 +141,11 @@ class VisualShader : public Shader { HashMap modes; HashSet flags; + bool stencil_enabled = false; + HashMap stencil_modes; + HashSet stencil_flags; + int stencil_reference = 1; + HashMap varyings; List varyings_list; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index f97ed3d21564..2f681d129531 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -827,6 +827,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con if (p_render_list == RENDER_LIST_OPAQUE) { // Opaque fills motion and alpha lists. render_list[RENDER_LIST_MOTION].clear(); + render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].clear(); // Opaque fills opaque_no_depth_prepass too. render_list[RENDER_LIST_ALPHA].clear(); } } @@ -1031,7 +1032,12 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con } if (!force_alpha && (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE))) { - rl->add_element(surf); + if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_NO_DEPTH_PREPASS) { + render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].add_element(surf); + scene_state.used_opaque_no_depth_prepass = true; + } else { + rl->add_element(surf); + } } if (force_alpha || (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) { @@ -1740,11 +1746,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_sdfgi, using_sdfgi || using_voxelgi, using_motion_pass); render_list[RENDER_LIST_OPAQUE].sort_by_key(); render_list[RENDER_LIST_MOTION].sort_by_key(); + render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].sort_by_key(); render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); int *render_info = p_render_data->render_info ? p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE] : (int *)nullptr; _fill_instance_data(RENDER_LIST_OPAQUE, render_info); _fill_instance_data(RENDER_LIST_MOTION, render_info); + _fill_instance_data(RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS, render_info); _fill_instance_data(RENDER_LIST_ALPHA); RD::get_singleton()->draw_command_end_label(); @@ -2020,8 +2028,17 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags; RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer; - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags); - _render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0); + + { + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags); + _render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, scene_state.used_opaque_no_depth_prepass ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, scene_state.used_opaque_no_depth_prepass ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_STORE, c, 0.0, 0); + } + + if (scene_state.used_opaque_no_depth_prepass) { + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS, p_render_data, radiance_texture, samplers, true); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].elements.ptr(), render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].element_info.ptr(), render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags); + _render_list_with_draw_list(&render_list_params, opaque_framebuffer, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_STORE, c, 0.0, 0); + } } RD::get_singleton()->draw_command_end_label(); @@ -3786,6 +3803,13 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_MOTION_VECTOR; } + if (p_material->shader_data->stencil_enabled) { + // Stencil materials which read from the stencil buffer must be sorted after all other depth-related operations. + if (p_material->shader_data->stencil_flags & SceneShaderForwardClustered::ShaderData::STENCIL_FLAG_READ) { + flags |= GeometryInstanceSurfaceDataCache::FLAG_NO_DEPTH_PREPASS; + } + } + SceneShaderForwardClustered::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_position && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing && p_material->shader_data->cull_mode == SceneShaderForwardClustered::ShaderData::CULL_BACK && !p_material->shader_data->uses_point_size && !p_material->shader_data->uses_world_coordinates) { diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 0aa4a0667ec0..dc37dd553f28 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -86,6 +86,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { enum RenderListType { RENDER_LIST_OPAQUE, //used for opaque objects RENDER_LIST_MOTION, //used for opaque objects with motion + RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS, // Used for opaque objects which cannot use depth prepass. RENDER_LIST_ALPHA, //used for transparent objects RENDER_LIST_SECONDARY, //used for shadows and other objects RENDER_LIST_MAX @@ -339,6 +340,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { bool used_depth_texture = false; bool used_sss = false; bool used_lightmap = false; + bool used_opaque_no_depth_prepass = false; struct ShadowPass { uint32_t element_from; @@ -408,6 +410,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { FLAG_USES_DOUBLE_SIDED_SHADOWS = 32768, FLAG_USES_PARTICLE_TRAILS = 65536, FLAG_USES_MOTION_VECTOR = 131072, + FLAG_NO_DEPTH_PREPASS = 262144, }; union { 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 c72795262b2d..fca3fe4519c0 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 @@ -82,6 +82,12 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; @@ -149,6 +155,20 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.write_flag_pointers["VERTEX"] = &uses_vertex; actions.write_flag_pointers["POSITION"] = &uses_position; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton; @@ -172,6 +192,11 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_normal |= uses_normal_map; uses_tangent |= uses_normal_map; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + #if 0 print_line("**compiling shader:"); print_line("**defines:\n"); @@ -298,7 +323,48 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { depth_stencil_state.depth_compare_operator = depth_function_rd_table[depth_function]; } - bool depth_pre_pass_enabled = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")); + depth_stencil_state.enable_stencil = stencil_enabled; + if (stencil_enabled) { + RD::CompareOperator stencil_compare_rd_table[STENCIL_COMPARE_MAX] = { + RD::COMPARE_OP_LESS, + RD::COMPARE_OP_EQUAL, + RD::COMPARE_OP_LESS_OR_EQUAL, + RD::COMPARE_OP_GREATER, + RD::COMPARE_OP_NOT_EQUAL, + RD::COMPARE_OP_GREATER_OR_EQUAL, + RD::COMPARE_OP_ALWAYS, + }; + + uint32_t stencil_mask = 255; + + RD::PipelineDepthStencilState::StencilOperationState op; + op.fail = RD::STENCIL_OP_KEEP; + op.pass = RD::STENCIL_OP_KEEP; + op.depth_fail = RD::STENCIL_OP_KEEP; + op.compare = stencil_compare_rd_table[stencil_compare]; + op.compare_mask = 0; + op.write_mask = 0; + op.reference = stencil_reference; + + if (stencil_flags & STENCIL_FLAG_READ) { + op.compare_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE) { + op.pass = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) { + op.depth_fail = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + depth_stencil_state.front_op = op; + depth_stencil_state.back_op = op; + } + + bool depth_pre_pass_enabled = !stencil_enabled && bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")); for (int i = 0; i < CULL_VARIANT_MAX; i++) { RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 1af184a2d7d4..ebe7af666c5f 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -153,6 +153,23 @@ class SceneShaderForwardClustered { ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE }; + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + }; + + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators. + }; + bool valid = false; RID version; uint64_t vertex_input_mask = 0; @@ -198,6 +215,11 @@ class SceneShaderForwardClustered { bool uses_screen_texture_mipmaps = false; Cull cull_mode = CULL_DISABLED; + bool stencil_enabled = false; + uint32_t stencil_flags = 0; + StencilCompare stencil_compare = STENCIL_COMPARE_LESS; + uint32_t stencil_reference = 0; + uint64_t last_pass = 0; uint32_t index = 0; 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 fc28fd4ef0f6..7d73b11f243d 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 @@ -83,6 +83,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; @@ -149,6 +155,20 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection; actions.write_flag_pointers["VERTEX"] = &uses_vertex; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton; @@ -172,6 +192,11 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_normal |= uses_normal_map; uses_tangent |= uses_normal_map; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + #ifdef DEBUG_ENABLED if (uses_sss) { WARN_PRINT_ONCE_ED("Sub-surface scattering is only available when using the Forward+ rendering backend."); @@ -309,6 +334,47 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { depth_stencil_state.depth_compare_operator = depth_function_rd_table[depth_function]; } + depth_stencil_state.enable_stencil = stencil_enabled; + if (stencil_enabled) { + RD::CompareOperator stencil_compare_rd_table[STENCIL_COMPARE_MAX] = { + RD::COMPARE_OP_LESS, + RD::COMPARE_OP_EQUAL, + RD::COMPARE_OP_LESS_OR_EQUAL, + RD::COMPARE_OP_GREATER, + RD::COMPARE_OP_NOT_EQUAL, + RD::COMPARE_OP_GREATER_OR_EQUAL, + RD::COMPARE_OP_ALWAYS, + }; + + uint32_t stencil_mask = 255; + + RD::PipelineDepthStencilState::StencilOperationState op; + op.fail = RD::STENCIL_OP_KEEP; + op.pass = RD::STENCIL_OP_KEEP; + op.depth_fail = RD::STENCIL_OP_KEEP; + op.compare = stencil_compare_rd_table[stencil_compare]; + op.compare_mask = 0; + op.write_mask = 0; + op.reference = stencil_reference; + + if (stencil_flags & STENCIL_FLAG_READ) { + op.compare_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE) { + op.pass = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) { + op.depth_fail = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + depth_stencil_state.front_op = op; + depth_stencil_state.back_op = op; + } + for (int i = 0; i < CULL_VARIANT_MAX; i++) { RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = { { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK }, diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index e6ff7e547e90..d9972a8585ab 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -108,6 +108,23 @@ class SceneShaderForwardMobile { ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE }; + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + }; + + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators. + }; + bool valid = false; RID version; uint64_t vertex_input_mask = 0; @@ -150,6 +167,11 @@ class SceneShaderForwardMobile { bool writes_modelview_or_projection = false; bool uses_world_coordinates = false; + bool stencil_enabled = false; + uint32_t stencil_flags = 0; + StencilCompare stencil_compare = STENCIL_COMPARE_LESS; + uint32_t stencil_reference = 0; + uint64_t last_pass = 0; uint32_t index = 0; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index a4ee33ecc0a2..991f019b51be 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -453,6 +453,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene case SL::Node::NODE_TYPE_SHADER: { SL::ShaderNode *pnode = (SL::ShaderNode *)p_node; + // Render modes. + for (int i = 0; i < pnode->render_modes.size(); i++) { if (p_default_actions.render_mode_defines.has(pnode->render_modes[i]) && !used_rmode_defines.has(pnode->render_modes[i])) { r_gen_code.defines.push_back(p_default_actions.render_mode_defines[pnode->render_modes[i]]); @@ -469,6 +471,21 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } } + // Stencil modes. + + for (int i = 0; i < pnode->stencil_modes.size(); i++) { + if (p_actions.stencil_mode_values.has(pnode->stencil_modes[i])) { + Pair &p = p_actions.stencil_mode_values[pnode->stencil_modes[i]]; + *p.first = p.second; + } + } + + // Stencil reference value. + + if (p_actions.stencil_reference && pnode->stencil_reference != -1) { + *p_actions.stencil_reference = pnode->stencil_reference; + } + // structs for (int i = 0; i < pnode->vstructs.size(); i++) { @@ -1459,6 +1476,7 @@ Error ShaderCompiler::compile(RS::ShaderMode p_mode, const String &p_code, Ident SL::ShaderCompileInfo info; info.functions = ShaderTypes::get_singleton()->get_functions(p_mode); info.render_modes = ShaderTypes::get_singleton()->get_modes(p_mode); + info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(p_mode); info.shader_types = ShaderTypes::get_singleton()->get_types(); info.global_shader_uniform_type_func = _get_global_shader_uniform_type; diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index 66106d7eb734..39eea01b55b0 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -51,6 +51,8 @@ class ShaderCompiler { HashMap render_mode_flags; HashMap usage_flag_pointers; HashMap write_flag_pointers; + HashMap> stencil_mode_values; + int *stencil_reference; HashMap *uniforms = nullptr; }; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index e35fde406f7d..72254a139698 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -334,6 +334,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} }, { TK_SHADER_TYPE, "shader_type", CF_SHADER_TYPE, {}, {} }, { TK_RENDER_MODE, "render_mode", CF_GLOBAL_SPACE, {}, {} }, + { TK_STENCIL_MODE, "stencil_mode", CF_GLOBAL_SPACE, {}, {} }, // uniform qualifiers @@ -3697,7 +3698,7 @@ bool ShaderLanguage::is_token_operator_assign(TokenType p_type) { } bool ShaderLanguage::is_token_hint(TokenType p_type) { - return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE); + return int(p_type) > int(TK_STENCIL_MODE) && int(p_type) < int(TK_SHADER_TYPE); } bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) { @@ -8139,7 +8140,7 @@ Error ShaderLanguage::_validate_precision(DataType p_type, DataPrecision p_preci return OK; } -Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types) { +Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const Vector &p_stencil_modes, const HashSet &p_shader_types) { Token tk; TkPos prev_pos; Token next; @@ -8208,7 +8209,8 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo(); - HashMap defined_modes; + HashMap defined_render_modes; + HashMap defined_stencil_modes; while (tk.type != TK_EOF) { switch (tk.type) { @@ -8217,83 +8219,64 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f keyword_completion_context = CF_UNSPECIFIED; #endif // DEBUG_ENABLED while (true) { - StringName mode; - _get_completable_identifier(nullptr, COMPLETION_RENDER_MODE, mode); - - if (mode == StringName()) { - _set_error(RTR("Expected an identifier for render mode.")); - return ERR_PARSE_ERROR; + Error error = _parse_shader_mode(false, p_render_modes, defined_render_modes); + if (error != OK) { + return error; } - const String smode = String(mode); + tk = _get_token(); - if (shader->render_modes.has(mode)) { - _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode)); + if (tk.type == TK_COMMA) { + // All good, do nothing. + } else if (tk.type == TK_SEMICOLON) { + break; // Done. + } else { + _set_error(vformat(RTR("Unexpected token: '%s'."), get_token_text(tk))); return ERR_PARSE_ERROR; } + } +#ifdef DEBUG_ENABLED + keyword_completion_context = CF_GLOBAL_SPACE; +#endif // DEBUG_ENABLED + } break; + case TK_STENCIL_MODE: { +#ifdef DEBUG_ENABLED + keyword_completion_context = CF_UNSPECIFIED; +#endif // DEBUG_ENABLED + while (true) { + TkPos pos = _get_tkpos(); + tk = _get_token(); - bool found = false; - - if (is_shader_inc) { - for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { - const Vector modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + if (tk.is_integer_constant()) { + const int reference_value = tk.constant; - for (int j = 0; j < modes.size(); j++) { - const ModeInfo &info = modes[j]; - const String name = String(info.name); + if (shader->stencil_reference != -1) { + _set_error(vformat(RTR("Duplicated stencil mode reference value: '%s'."), reference_value)); + return ERR_PARSE_ERROR; + } - if (smode.begins_with(name)) { - if (!info.options.is_empty()) { - if (info.options.has(smode.substr(name.length() + 1))) { - found = true; + if (reference_value < 0) { + _set_error(vformat(RTR("Stencil mode reference value cannot be negative: '%s'."), reference_value)); + return ERR_PARSE_ERROR; + } - if (defined_modes.has(name)) { - _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name])); - return ERR_PARSE_ERROR; - } - defined_modes.insert(name, smode); - break; - } - } else { - found = true; - break; - } - } - } + if (reference_value > 255) { + _set_error(vformat(RTR("Stencil mode reference value cannot be greater than 255: '%s'."), reference_value)); + return ERR_PARSE_ERROR; } - } else { - for (int i = 0; i < p_render_modes.size(); i++) { - const ModeInfo &info = p_render_modes[i]; - const String name = String(info.name); - if (smode.begins_with(name)) { - if (!info.options.is_empty()) { - if (info.options.has(smode.substr(name.length() + 1))) { - found = true; + shader->stencil_reference = reference_value; + } else { + _set_tkpos(pos); - if (defined_modes.has(name)) { - _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name])); - return ERR_PARSE_ERROR; - } - defined_modes.insert(name, smode); - break; - } - } else { - found = true; - break; - } - } + Error error = _parse_shader_mode(true, p_stencil_modes, defined_stencil_modes); + if (error != OK) { + return error; } } - if (!found) { - _set_error(vformat(RTR("Invalid render mode: '%s'."), smode)); - return ERR_PARSE_ERROR; - } - - shader->render_modes.push_back(mode); - tk = _get_token(); + if (tk.type == TK_COMMA) { //all good, do nothing } else if (tk.type == TK_SEMICOLON) { @@ -10007,6 +9990,112 @@ Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperat return FAILED; } +Error ShaderLanguage::_parse_shader_mode(bool p_is_stencil, const Vector &p_modes, HashMap &r_defined_modes) { + StringName mode; + _get_completable_identifier(nullptr, p_is_stencil ? COMPLETION_STENCIL_MODE : COMPLETION_RENDER_MODE, mode); + + if (mode == StringName()) { + if (p_is_stencil) { + _set_error(RTR("Expected an identifier for stencil mode.")); + } else { + _set_error(RTR("Expected an identifier for render mode.")); + } + return ERR_PARSE_ERROR; + } + + const String smode = String(mode); + + Vector ¤t_modes = p_is_stencil ? shader->stencil_modes : shader->render_modes; + + if (current_modes.has(mode)) { + if (p_is_stencil) { + _set_error(vformat(RTR("Duplicated stencil mode: '%s'."), smode)); + } else { + _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode)); + } + return ERR_PARSE_ERROR; + } + + bool found = false; + + if (is_shader_inc) { + for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { + const Vector modes = p_is_stencil ? ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)) : ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + + for (int j = 0; j < modes.size(); j++) { + const ModeInfo &info = modes[j]; + const String name = String(info.name); + + if (smode.begins_with(name)) { + if (!info.options.is_empty()) { + if (info.options.has(smode.substr(name.length() + 1))) { + found = true; + + if (r_defined_modes.has(name)) { + if (p_is_stencil) { + _set_error(vformat(RTR("Redefinition of stencil mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } else { + _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } + return ERR_PARSE_ERROR; + } + r_defined_modes.insert(name, smode); + break; + } + } else { + found = true; + break; + } + } + } + } + } else { + for (int i = 0; i < p_modes.size(); i++) { + const ModeInfo &info = p_modes[i]; + const String name = String(info.name); + + if (smode.begins_with(name)) { + if (!info.options.is_empty()) { + if (info.options.has(smode.substr(name.length() + 1))) { + found = true; + + if (r_defined_modes.has(name)) { + if (p_is_stencil) { + _set_error(vformat(RTR("Redefinition of stencil mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } else { + _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } + return ERR_PARSE_ERROR; + } + r_defined_modes.insert(name, smode); + break; + } + } else { + found = true; + break; + } + } + } + } + + if (!found) { + if (p_is_stencil) { + _set_error(vformat(RTR("Invalid stencil mode: '%s'."), smode)); + } else { + _set_error(vformat(RTR("Invalid render mode: '%s'."), smode)); + } + return ERR_PARSE_ERROR; + } + + if (p_is_stencil) { + shader->stencil_modes.push_back(mode); + } else { + shader->render_modes.push_back(mode); + } + + return OK; +} + // skips over whitespace and /* */ and // comments static int _get_first_ident_pos(const String &p_code) { int idx = 0; @@ -10156,7 +10245,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i nodes = nullptr; shader = alloc_node(); - Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types); + Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.stencil_modes, p_info.shader_types); #ifdef DEBUG_ENABLED if (check_warnings) { @@ -10181,7 +10270,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ global_shader_uniform_get_type_func = p_info.global_shader_uniform_type_func; shader = alloc_node(); - _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types); + _parse_shader(p_info.functions, p_info.render_modes, p_info.stencil_modes, p_info.shader_types); #ifdef DEBUG_ENABLED // Adds context keywords. @@ -10282,6 +10371,71 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ return OK; } break; + case COMPLETION_STENCIL_MODE: { + if (is_shader_inc) { + for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { + const Vector modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)); + + for (int j = 0; j < modes.size(); j++) { + const ModeInfo &info = modes[j]; + + if (!info.options.is_empty()) { + bool found = false; + + for (int k = 0; k < info.options.size(); k++) { + if (shader->stencil_modes.has(String(info.name) + "_" + String(info.options[k]))) { + found = true; + } + } + + if (!found) { + for (int k = 0; k < info.options.size(); k++) { + ScriptLanguage::CodeCompletionOption option(String(info.name) + "_" + String(info.options[k]), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(option); + } + } + } else { + const String name = String(info.name); + + if (!shader->stencil_modes.has(name)) { + ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(option); + } + } + } + } + } else { + for (int i = 0; i < p_info.stencil_modes.size(); i++) { + const ModeInfo &info = p_info.stencil_modes[i]; + + if (!info.options.is_empty()) { + bool found = false; + + for (int j = 0; j < info.options.size(); j++) { + if (shader->stencil_modes.has(String(info.name) + "_" + String(info.options[j]))) { + found = true; + } + } + + if (!found) { + for (int j = 0; j < info.options.size(); j++) { + ScriptLanguage::CodeCompletionOption option(String(info.name) + "_" + String(info.options[j]), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(option); + } + } + } else { + const String name = String(info.name); + + if (!shader->stencil_modes.has(name)) { + ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(option); + } + } + } + } + + return OK; + } break; case COMPLETION_STRUCT: { if (shader->structs.has(completion_struct)) { StructNode *node = shader->structs[completion_struct].shader_struct; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index d8d7cc197bfc..696ee66db4a9 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -162,6 +162,7 @@ class ShaderLanguage { TK_ARG_OUT, TK_ARG_INOUT, TK_RENDER_MODE, + TK_STENCIL_MODE, TK_HINT_DEFAULT_WHITE_TEXTURE, TK_HINT_DEFAULT_BLACK_TEXTURE, TK_HINT_DEFAULT_TRANSPARENT_TEXTURE, @@ -713,6 +714,8 @@ class ShaderLanguage { HashMap structs; HashMap functions; Vector render_modes; + Vector stencil_modes; + int stencil_reference = -1; Vector vfunctions; Vector vconstants; @@ -745,6 +748,7 @@ class ShaderLanguage { COMPLETION_NONE, COMPLETION_SHADER_TYPE, COMPLETION_RENDER_MODE, + COMPLETION_STENCIL_MODE, COMPLETION_MAIN_FUNCTION, COMPLETION_IDENTIFIER, COMPLETION_FUNCTION_CALL, @@ -1135,11 +1139,13 @@ class ShaderLanguage { String _get_shader_type_list(const HashSet &p_shader_types) const; String _get_qualifier_str(ArgumentQualifier p_qualifier) const; - Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types); + Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const Vector &p_stencil_modes, const HashSet &p_shader_types); Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op); Error _find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op); + Error _parse_shader_mode(bool p_is_stencil, const Vector &p_modes, HashMap &r_defined_modes); + public: #ifdef DEBUG_ENABLED List::Element *get_warnings_ptr(); @@ -1161,6 +1167,7 @@ class ShaderLanguage { struct ShaderCompileInfo { HashMap functions; Vector render_modes; + Vector stencil_modes; VaryingFunctionNames varying_function_names = VaryingFunctionNames(); HashSet shader_types; GlobalShaderUniformGetTypeFunc global_shader_uniform_type_func = nullptr; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index a3f92b8c5303..6bfd05e0f5fa 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -39,6 +39,10 @@ const Vector &ShaderTypes::get_modes(RS::ShaderMode p_ return shader_modes[p_mode].modes; } +const Vector &ShaderTypes::get_stencil_modes(RS::ShaderMode p_mode) const { + return shader_modes[p_mode].stencil_modes; +} + const HashSet &ShaderTypes::get_types() const { return shader_types; } @@ -232,6 +236,10 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("alpha_to_coverage_and_one") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("debug_shadow_splits") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("fog_disabled") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("read") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("write") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("write_depth_fail") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("compare"), { "less", "equal", "less_or_equal", "greater", "not_equal", "greater_or_equal", "always" } }); } /************ CANVAS ITEM **************************/ diff --git a/servers/rendering/shader_types.h b/servers/rendering/shader_types.h index 5a7423b6618d..f250329e5a29 100644 --- a/servers/rendering/shader_types.h +++ b/servers/rendering/shader_types.h @@ -39,6 +39,7 @@ class ShaderTypes { struct Type { HashMap functions; Vector modes; + Vector stencil_modes; }; HashMap shader_modes; @@ -53,6 +54,7 @@ class ShaderTypes { const HashMap &get_functions(RS::ShaderMode p_mode) const; const Vector &get_modes(RS::ShaderMode p_mode) const; + const Vector &get_stencil_modes(RS::ShaderMode p_mode) const; const HashSet &get_types() const; const List &get_types_list() const;