diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index fc8af02869eb..71a6498a4219 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -151,6 +151,10 @@ Determines when depth rendering takes place. See [enum DepthDrawMode]. See also [member transparency]. + + Determines which comparison operator is used when testing depth. See [enum DepthFunction]. + [b]Note:[/b] Changing [member depth_function] to a non-default value only has a visible effect when used on a transparent material, or a material that has [member depth_draw_mode] set to [constant DEPTH_DRAW_DISABLED]. + Texture that specifies the color of the detail overlay. [member detail_albedo]'s alpha channel is used as a mask, even when the material is opaque. To use a dedicated texture as a mask, see [member detail_mask]. [b]Note:[/b] [member detail_albedo] is [i]not[/i] modulated by [member albedo_color]. @@ -359,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]. @@ -631,6 +653,30 @@ Objects will not write their depth to the depth buffer, even during the depth prepass (if enabled). + + Depth check succeeds if less than or equal to existing depth. + + + Depth check succeeds if less than existing depth. + + + Depth check succeeds if equal to existing depth. + + + Depth check succeeds if greater than existing depth. + + + Depth check succeeds if not equal to existing depth. + + + Default depth function. Depth check succeeds if greater than or equal to existing depth. + + + Depth check always succeeds. + + + Depth check never succeeds. + Default cull mode. The back of the object is culled when not visible. Back face triangles will be culled when facing the camera. This results in only the front side of triangles being drawn. For closed-surface meshes, this means that only the exterior of the mesh will be visible. @@ -777,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/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index a6796a1a6bbb..53c334534067 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2190,7 +2190,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, scene_state.reset_gl_state(); scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); - glDepthFunc(GL_GREATER); + scene_state.set_gl_depth_func(GLES3::SceneShaderData::DEPTH_FUNCTION_GREATER); glColorMask(0, 0, 0, 0); glDrawBuffers(0, nullptr); @@ -2493,7 +2493,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); scene_state.enable_gl_blend(false); - glDepthFunc(GL_GEQUAL); + scene_state.set_gl_depth_func(GLES3::SceneShaderData::DEPTH_FUNCTION_GREATER_OR_EQUAL); scene_state.enable_gl_scissor_test(false); glColorMask(0, 0, 0, 0); @@ -2531,7 +2531,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.enable_gl_scissor_test(false); scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); - glDepthFunc(GL_GEQUAL); + scene_state.set_gl_depth_func(GLES3::SceneShaderData::DEPTH_FUNCTION_GREATER_OR_EQUAL); { GLuint db = GL_COLOR_ATTACHMENT0; @@ -2591,6 +2591,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ RENDER_TIMESTAMP("Render Sky"); scene_state.enable_gl_depth_test(true); + scene_state.set_gl_depth_func(GLES3::SceneShaderData::DEPTH_FUNCTION_GREATER_OR_EQUAL); scene_state.enable_gl_blend(false); scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK); @@ -2971,6 +2972,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, scene_state.enable_gl_depth_test(shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_ENABLED); } + scene_state.set_gl_depth_func(shader->depth_function); + if constexpr (p_pass_mode != PASS_MODE_SHADOW) { if (shader->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE) { scene_state.enable_gl_depth_draw((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || p_pass_mode == PASS_MODE_DEPTH); @@ -3606,7 +3609,7 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, scene_state.reset_gl_state(); scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); - glDepthFunc(GL_GREATER); + scene_state.set_gl_depth_func(GLES3::SceneShaderData::DEPTH_FUNCTION_GREATER); glDrawBuffers(0, nullptr); @@ -3652,7 +3655,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray draw_buffers; draw_buffers.push_back(GL_COLOR_ATTACHMENT0); @@ -3745,7 +3748,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref(&depth_testi, DEPTH_TEST_DISABLED); + actions.render_mode_values["depth_function_less_or_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_LESS_OR_EQUAL); + actions.render_mode_values["depth_function_less"] = Pair(&depth_functioni, DEPTH_FUNCTION_LESS); + actions.render_mode_values["depth_function_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_EQUAL); + actions.render_mode_values["depth_function_greater"] = Pair(&depth_functioni, DEPTH_FUNCTION_GREATER); + actions.render_mode_values["depth_function_not_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_NOT_EQUAL); + actions.render_mode_values["depth_function_greater_or_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_GREATER_OR_EQUAL); + actions.render_mode_values["depth_function_always"] = Pair(&depth_functioni, DEPTH_FUNCTION_ALWAYS); + actions.render_mode_values["depth_function_never"] = Pair(&depth_functioni, DEPTH_FUNCTION_NEVER); + actions.render_mode_values["cull_disabled"] = Pair(&cull_modei, CULL_DISABLED); actions.render_mode_values["cull_front"] = Pair(&cull_modei, CULL_FRONT); actions.render_mode_values["cull_back"] = Pair(&cull_modei, CULL_BACK); @@ -2978,6 +2988,7 @@ void SceneShaderData::set_code(const String &p_code) { alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei); depth_draw = DepthDraw(depth_drawi); depth_test = DepthTest(depth_testi); + depth_function = DepthFunction(depth_functioni); cull_mode = Cull(cull_modei); vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals. diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 392ebcc570d9..28a2f5331689 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -263,6 +263,18 @@ struct SceneShaderData : public ShaderData { DEPTH_TEST_ENABLED }; + enum DepthFunction { + DEPTH_FUNCTION_LESS_OR_EQUAL, + DEPTH_FUNCTION_LESS, + DEPTH_FUNCTION_EQUAL, + DEPTH_FUNCTION_GREATER, + DEPTH_FUNCTION_NOT_EQUAL, + DEPTH_FUNCTION_GREATER_OR_EQUAL, + DEPTH_FUNCTION_ALWAYS, + DEPTH_FUNCTION_NEVER, + DEPTH_FUNCTION_MAX + }; + enum Cull { CULL_DISABLED, CULL_FRONT, @@ -292,6 +304,7 @@ struct SceneShaderData : public ShaderData { AlphaAntiAliasing alpha_antialiasing_mode; DepthDraw depth_draw; DepthTest depth_test; + DepthFunction depth_function; Cull cull_mode; bool uses_point_size; 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 7d121c9d87fb..85e365a01985 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -727,6 +727,35 @@ void BaseMaterial3D::_update_shader() { break; // Internal value, skip. } + switch (depth_function) { + case DEPTH_FUNCTION_LESS_OR_EQUAL: + code += ",depth_function_less_or_equal"; + break; + case DEPTH_FUNCTION_LESS: + code += ",depth_function_less"; + break; + case DEPTH_FUNCTION_EQUAL: + code += ",depth_function_equal"; + break; + case DEPTH_FUNCTION_GREATER: + code += ",depth_function_greater"; + break; + case DEPTH_FUNCTION_NOT_EQUAL: + code += ",depth_function_not_equal"; + break; + case DEPTH_FUNCTION_GREATER_OR_EQUAL: + code += ",depth_function_greater_or_equal"; + break; + case DEPTH_FUNCTION_ALWAYS: + code += ",depth_function_always"; + break; + case DEPTH_FUNCTION_NEVER: + code += ",depth_function_never"; + break; + case DEPTH_FUNCTION_MAX: + break; // Internal value, skip. + } + switch (cull_mode) { case CULL_BACK: code += ", cull_back"; @@ -816,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; @@ -2170,6 +2243,19 @@ BaseMaterial3D::DepthDrawMode BaseMaterial3D::get_depth_draw_mode() const { return depth_draw_mode; } +void BaseMaterial3D::set_depth_function(DepthFunction p_func) { + if (depth_function == p_func) { + return; + } + + depth_function = p_func; + _queue_shader_change(); +} + +BaseMaterial3D::DepthFunction BaseMaterial3D::get_depth_function() const { + return depth_function; +} + void BaseMaterial3D::set_cull_mode(CullMode p_mode) { if (cull_mode == p_mode) { return; @@ -2391,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; } @@ -2835,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()) { @@ -2935,6 +3202,9 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_depth_draw_mode", "depth_draw_mode"), &BaseMaterial3D::set_depth_draw_mode); ClassDB::bind_method(D_METHOD("get_depth_draw_mode"), &BaseMaterial3D::get_depth_draw_mode); + ClassDB::bind_method(D_METHOD("set_depth_function", "depth_function"), &BaseMaterial3D::set_depth_function); + ClassDB::bind_method(D_METHOD("get_depth_function"), &BaseMaterial3D::get_depth_function); + ClassDB::bind_method(D_METHOD("set_cull_mode", "cull_mode"), &BaseMaterial3D::set_cull_mode); ClassDB::bind_method(D_METHOD("get_cull_mode"), &BaseMaterial3D::get_cull_mode); @@ -3055,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"); @@ -3064,6 +3352,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Premultiplied Alpha"), "set_blend_mode", "get_blend_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_function", PROPERTY_HINT_ENUM, "Less or Equal,Less,Equal,Greater,Not Equal,Greater or Equal,Always,Never"), "set_depth_function", "get_depth_function"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST); ADD_GROUP("Shading", ""); @@ -3229,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); @@ -3300,6 +3598,15 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(DEPTH_DRAW_ALWAYS); BIND_ENUM_CONSTANT(DEPTH_DRAW_DISABLED); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_LESS_OR_EQUAL); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_LESS); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_EQUAL); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_GREATER); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_NOT_EQUAL); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_GREATER_OR_EQUAL); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_ALWAYS); + BIND_ENUM_CONSTANT(DEPTH_FUNCTION_NEVER); + BIND_ENUM_CONSTANT(CULL_BACK); BIND_ENUM_CONSTANT(CULL_FRONT); BIND_ENUM_CONSTANT(CULL_DISABLED); @@ -3355,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) : @@ -3420,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 50a774e961f8..7682a334e67f 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -231,6 +231,18 @@ class BaseMaterial3D : public Material { DEPTH_DRAW_MAX }; + enum DepthFunction { + DEPTH_FUNCTION_LESS_OR_EQUAL, + DEPTH_FUNCTION_LESS, + DEPTH_FUNCTION_EQUAL, + DEPTH_FUNCTION_GREATER, + DEPTH_FUNCTION_NOT_EQUAL, + DEPTH_FUNCTION_GREATER_OR_EQUAL, + DEPTH_FUNCTION_ALWAYS, + DEPTH_FUNCTION_NEVER, + DEPTH_FUNCTION_MAX + }; + enum CullMode { CULL_BACK, CULL_FRONT, @@ -310,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 @@ -320,6 +359,7 @@ class BaseMaterial3D : public Material { uint64_t shading_mode : get_num_bits(SHADING_MODE_MAX - 1); uint64_t blend_mode : get_num_bits(BLEND_MODE_MAX - 1); uint64_t depth_draw_mode : get_num_bits(DEPTH_DRAW_MAX - 1); + uint64_t depth_function : get_num_bits(DEPTH_FUNCTION_MAX - 1); uint64_t cull_mode : get_num_bits(CULL_MAX - 1); uint64_t diffuse_mode : get_num_bits(DIFFUSE_MAX - 1); uint64_t specular_mode : get_num_bits(SPECULAR_MAX - 1); @@ -328,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; @@ -370,6 +417,7 @@ class BaseMaterial3D : public Material { mk.detail_uv = detail_uv; mk.blend_mode = blend_mode; mk.depth_draw_mode = depth_draw_mode; + mk.depth_function = depth_function; mk.cull_mode = cull_mode; mk.texture_filter = texture_filter; mk.transparency = transparency; @@ -387,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); @@ -536,6 +589,7 @@ class BaseMaterial3D : public Material { BlendMode blend_mode = BLEND_MODE_MIX; BlendMode detail_blend_mode = BLEND_MODE_MIX; DepthDrawMode depth_draw_mode = DEPTH_DRAW_OPAQUE_ONLY; + DepthFunction depth_function = DEPTH_FUNCTION_GREATER_OR_EQUAL; CullMode cull_mode = CULL_BACK; bool flags[FLAG_MAX] = {}; SpecularMode specular_mode = SPECULAR_SCHLICK_GGX; @@ -550,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: @@ -670,6 +735,9 @@ class BaseMaterial3D : public Material { void set_depth_draw_mode(DepthDrawMode p_mode); DepthDrawMode get_depth_draw_mode() const; + void set_depth_function(DepthFunction p_func); + DepthFunction get_depth_function() const; + void set_cull_mode(CullMode p_mode); CullMode get_cull_mode() const; @@ -760,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); @@ -792,6 +878,7 @@ VARIANT_ENUM_CAST(BaseMaterial3D::DetailUV) VARIANT_ENUM_CAST(BaseMaterial3D::Feature) VARIANT_ENUM_CAST(BaseMaterial3D::BlendMode) VARIANT_ENUM_CAST(BaseMaterial3D::DepthDrawMode) +VARIANT_ENUM_CAST(BaseMaterial3D::DepthFunction) VARIANT_ENUM_CAST(BaseMaterial3D::CullMode) VARIANT_ENUM_CAST(BaseMaterial3D::Flags) VARIANT_ENUM_CAST(BaseMaterial3D::DiffuseMode) @@ -800,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 42e1f7b6dc4f..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 @@ -53,6 +53,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { int blend_mode = BLEND_MODE_MIX; int depth_testi = DEPTH_TEST_ENABLED; + int depth_functioni = DEPTH_FUNCTION_GREATER_OR_EQUAL; int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF; int cull_modei = CULL_BACK; @@ -81,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; @@ -101,6 +108,15 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.render_mode_values["depth_test_disabled"] = Pair(&depth_testi, DEPTH_TEST_DISABLED); + actions.render_mode_values["depth_function_less_or_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_LESS_OR_EQUAL); + actions.render_mode_values["depth_function_less"] = Pair(&depth_functioni, DEPTH_FUNCTION_LESS); + actions.render_mode_values["depth_function_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_EQUAL); + actions.render_mode_values["depth_function_greater"] = Pair(&depth_functioni, DEPTH_FUNCTION_GREATER); + actions.render_mode_values["depth_function_not_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_NOT_EQUAL); + actions.render_mode_values["depth_function_greater_or_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_GREATER_OR_EQUAL); + actions.render_mode_values["depth_function_always"] = Pair(&depth_functioni, DEPTH_FUNCTION_ALWAYS); + actions.render_mode_values["depth_function_never"] = Pair(&depth_functioni, DEPTH_FUNCTION_NEVER); + actions.render_mode_values["cull_disabled"] = Pair(&cull_modei, CULL_DISABLED); actions.render_mode_values["cull_front"] = Pair(&cull_modei, CULL_FRONT); actions.render_mode_values["cull_back"] = Pair(&cull_modei, CULL_BACK); @@ -139,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; @@ -151,6 +181,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { depth_draw = DepthDraw(depth_drawi); depth_test = DepthTest(depth_testi); + depth_function = DepthFunction(depth_functioni); cull_mode = Cull(cull_modei); uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; uses_screen_texture = gen_code.uses_screen_texture; @@ -161,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"); @@ -271,10 +307,64 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { if (depth_test != DEPTH_TEST_DISABLED) { depth_stencil_state.enable_depth_test = true; - depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL; depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false; + + RD::CompareOperator depth_function_rd_table[DEPTH_FUNCTION_MAX] = { + RD::COMPARE_OP_LESS_OR_EQUAL, + RD::COMPARE_OP_LESS, + RD::COMPARE_OP_EQUAL, + RD::COMPARE_OP_GREATER, + RD::COMPARE_OP_NOT_EQUAL, + RD::COMPARE_OP_GREATER_OR_EQUAL, + RD::COMPARE_OP_ALWAYS, + RD::COMPARE_OP_NEVER, + }; + + 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; } - bool depth_pre_pass_enabled = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")); + + 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 d5332032f9f0..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 @@ -121,6 +121,18 @@ class SceneShaderForwardClustered { DEPTH_TEST_ENABLED }; + enum DepthFunction { + DEPTH_FUNCTION_LESS_OR_EQUAL, + DEPTH_FUNCTION_LESS, + DEPTH_FUNCTION_EQUAL, + DEPTH_FUNCTION_GREATER, + DEPTH_FUNCTION_NOT_EQUAL, + DEPTH_FUNCTION_GREATER_OR_EQUAL, + DEPTH_FUNCTION_ALWAYS, + DEPTH_FUNCTION_NEVER, + DEPTH_FUNCTION_MAX + }; + enum Cull { CULL_DISABLED, CULL_FRONT, @@ -141,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; @@ -155,6 +184,7 @@ class SceneShaderForwardClustered { String code; DepthDraw depth_draw = DEPTH_DRAW_OPAQUE; + DepthFunction depth_function = DEPTH_FUNCTION_GREATER_OR_EQUAL; DepthTest depth_test = DEPTH_TEST_ENABLED; bool uses_point_size = false; @@ -185,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 cf661bb8f4bf..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 @@ -55,6 +55,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { int blend_mode = BLEND_MODE_MIX; int depth_testi = DEPTH_TEST_ENABLED; + int depth_functioni = DEPTH_FUNCTION_GREATER_OR_EQUAL; int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF; int cull = CULL_BACK; @@ -82,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; @@ -102,6 +109,15 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.render_mode_values["depth_test_disabled"] = Pair(&depth_testi, DEPTH_TEST_DISABLED); + actions.render_mode_values["depth_function_less_or_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_LESS_OR_EQUAL); + actions.render_mode_values["depth_function_less"] = Pair(&depth_functioni, DEPTH_FUNCTION_LESS); + actions.render_mode_values["depth_function_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_EQUAL); + actions.render_mode_values["depth_function_greater"] = Pair(&depth_functioni, DEPTH_FUNCTION_GREATER); + actions.render_mode_values["depth_function_not_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_NOT_EQUAL); + actions.render_mode_values["depth_function_greater_or_equal"] = Pair(&depth_functioni, DEPTH_FUNCTION_GREATER_OR_EQUAL); + actions.render_mode_values["depth_function_always"] = Pair(&depth_functioni, DEPTH_FUNCTION_ALWAYS); + actions.render_mode_values["depth_function_never"] = Pair(&depth_functioni, DEPTH_FUNCTION_NEVER); + actions.render_mode_values["cull_disabled"] = Pair(&cull, CULL_DISABLED); actions.render_mode_values["cull_front"] = Pair(&cull, CULL_FRONT); actions.render_mode_values["cull_back"] = Pair(&cull, CULL_BACK); @@ -139,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; @@ -152,6 +182,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { depth_draw = DepthDraw(depth_drawi); depth_test = DepthTest(depth_testi); + depth_function = DepthFunction(depth_functioni); uses_vertex_time = gen_code.uses_vertex_time; uses_fragment_time = gen_code.uses_fragment_time; uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; @@ -161,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."); @@ -282,8 +318,61 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { if (depth_test != DEPTH_TEST_DISABLED) { depth_stencil_state.enable_depth_test = true; - depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL; depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false; + + RD::CompareOperator depth_function_rd_table[DEPTH_FUNCTION_MAX] = { + RD::COMPARE_OP_LESS_OR_EQUAL, + RD::COMPARE_OP_LESS, + RD::COMPARE_OP_EQUAL, + RD::COMPARE_OP_GREATER, + RD::COMPARE_OP_NOT_EQUAL, + RD::COMPARE_OP_GREATER_OR_EQUAL, + RD::COMPARE_OP_ALWAYS, + RD::COMPARE_OP_NEVER, + }; + + 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++) { 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 833b06c1e31b..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 @@ -71,6 +71,18 @@ class SceneShaderForwardMobile { DEPTH_DRAW_ALWAYS }; + enum DepthFunction { + DEPTH_FUNCTION_LESS_OR_EQUAL, + DEPTH_FUNCTION_LESS, + DEPTH_FUNCTION_EQUAL, + DEPTH_FUNCTION_GREATER, + DEPTH_FUNCTION_NOT_EQUAL, + DEPTH_FUNCTION_GREATER_OR_EQUAL, + DEPTH_FUNCTION_ALWAYS, + DEPTH_FUNCTION_NEVER, + DEPTH_FUNCTION_MAX + }; + enum DepthTest { DEPTH_TEST_DISABLED, DEPTH_TEST_ENABLED @@ -96,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; @@ -109,6 +138,7 @@ class SceneShaderForwardMobile { String code; DepthDraw depth_draw; + DepthFunction depth_function; DepthTest depth_test; bool uses_point_size = false; @@ -137,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 076bd8def4a8..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, @@ -877,6 +881,13 @@ class ShaderLanguage { options.push_back(p_arg5); options.push_back(p_arg6); } + + ModeInfo(const StringName &p_name, std::initializer_list p_args) : + name(p_name) { + for (const StringName &arg : p_args) { + options.push_back(arg); + } + } }; struct FunctionInfo { @@ -1128,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(); @@ -1154,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 de396cd18baf..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; } @@ -211,6 +215,7 @@ ShaderTypes::ShaderTypes() { { shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("blend"), "mix", "add", "sub", "mul", "premul_alpha" }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_draw"), "opaque", "always", "never" }); + shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_function"), { "less_or_equal", "less", "equal", "greater", "not_equal", "greater_or_equal", "always", "never" } }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_prepass_alpha") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_test_disabled") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("sss_mode_skin") }); @@ -231,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;