Add stencil support for spatial materials

This commit is contained in:
Apples 2023-02-17 15:20:26 -06:00
parent 7574a5dbb3
commit d674c9e289
29 changed files with 1335 additions and 141 deletions

View file

@ -912,6 +912,56 @@ void BaseMaterial3D::_update_shader() {
code += ";\n";
if (stencil_mode != STENCIL_MODE_DISABLED && stencil_flags != 0) {
code += "stencil_mode ";
if (stencil_flags & STENCIL_FLAG_READ) {
code += "read";
}
if (stencil_flags & STENCIL_FLAG_WRITE) {
if (stencil_flags & STENCIL_FLAG_READ) {
code += ", ";
}
code += "write";
}
if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) {
if (stencil_flags & (STENCIL_FLAG_READ | STENCIL_FLAG_WRITE)) {
code += ", ";
}
code += "write_depth_fail";
}
switch (stencil_compare) {
case STENCIL_COMPARE_ALWAYS:
code += ", compare_always";
break;
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_MAX:
break;
}
code += vformat(", %s;\n", stencil_reference);
}
// Generate list of uniforms.
code += vformat(R"(
uniform vec4 albedo : source_color;
@ -2591,7 +2641,23 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
}
if (p_property.name == "depth_test" && flags[FLAG_DISABLE_DEPTH_TEST]) {
p_property.usage = PROPERTY_USAGE_NONE;
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (p_property.name == "stencil_reference" && stencil_mode == STENCIL_MODE_DISABLED) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if ((p_property.name == "stencil_flags" || p_property.name == "stencil_compare") && stencil_mode != STENCIL_MODE_CUSTOM) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (p_property.name == "stencil_color" && stencil_mode != STENCIL_MODE_OUTLINE && stencil_mode != STENCIL_MODE_XRAY) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (p_property.name == "stencil_outline_thickness" && stencil_mode != STENCIL_MODE_OUTLINE) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (p_property.name == "subsurf_scatter_transmittance_color" || p_property.name == "subsurf_scatter_transmittance_texture")) {
@ -3066,6 +3132,179 @@ RID BaseMaterial3D::get_rid() const {
return _get_material();
}
void BaseMaterial3D::_prepare_stencil_effect() {
const Ref<Material> 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<BaseMaterial3D> stencil_next_pass;
if (current_next_pass.is_null() || !current_next_pass->has_meta("_stencil_owned")) {
stencil_next_pass = Ref<BaseMaterial3D>(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> BaseMaterial3D::_get_stencil_next_pass() const {
const Ref<Material> current_next_pass = get_next_pass();
Ref<BaseMaterial3D> 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;
}
if ((p_stencil_flags & STENCIL_FLAG_READ) && (stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL))) {
p_stencil_flags = p_stencil_flags & STENCIL_FLAG_READ;
}
if ((p_stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL)) && (stencil_flags & STENCIL_FLAG_READ)) {
p_stencil_flags = p_stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL);
}
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<BaseMaterial3D> 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<BaseMaterial3D> 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<BaseMaterial3D> 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 {
const_cast<BaseMaterial3D *>(this)->_update_shader();
return shader_rid;
@ -3291,6 +3530,24 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fov_override", "scale"), &BaseMaterial3D::set_fov_override);
ClassDB::bind_method(D_METHOD("get_fov_override"), &BaseMaterial3D::get_fov_override);
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");
@ -3478,6 +3735,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,X-Ray,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, "Always,Less,Equal,Less Or Equal,Greater,Not Equal,Greater Or Equal"), "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);
@ -3612,6 +3878,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_ALWAYS);
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);
}
BaseMaterial3D::BaseMaterial3D(bool p_orm) :
@ -3680,6 +3963,8 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_z_clip_scale(1.0);
set_fov_override(75.0);
set_stencil_mode(STENCIL_MODE_DISABLED);
flags[FLAG_ALBEDO_TEXTURE_MSDF] = false;
flags[FLAG_USE_TEXTURE_REPEAT] = true;