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

@ -339,6 +339,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
@ -4128,7 +4129,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, Scalar *p_value) {
@ -9110,7 +9111,7 @@ bool ShaderLanguage::_parse_numeric_constant_expression(const FunctionInfo &p_fu
return true;
}
Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types) {
Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const Vector<ModeInfo> &p_stencil_modes, const HashSet<String> &p_shader_types) {
Token tk;
TkPos prev_pos;
Token next;
@ -9173,7 +9174,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo();
HashMap<String, String> defined_modes;
HashMap<String, String> defined_render_modes;
HashMap<String, String> defined_stencil_modes;
while (tk.type != TK_EOF) {
switch (tk.type) {
@ -9182,83 +9184,64 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &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);
if (shader->render_modes.has(mode)) {
_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<ModeInfo> modes = 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 (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;
}
}
}
}
} 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;
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 (!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) {
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();
if (tk.is_integer_constant()) {
const int reference_value = tk.constant;
if (shader->stencil_reference != -1) {
_set_error(vformat(RTR("Duplicated stencil mode reference value: '%s'."), reference_value));
return ERR_PARSE_ERROR;
}
if (reference_value < 0) {
_set_error(vformat(RTR("Stencil mode reference value cannot be negative: '%s'."), reference_value));
return ERR_PARSE_ERROR;
}
if (reference_value > 255) {
_set_error(vformat(RTR("Stencil mode reference value cannot be greater than 255: '%s'."), reference_value));
return ERR_PARSE_ERROR;
}
shader->stencil_reference = reference_value;
} else {
_set_tkpos(pos);
Error error = _parse_shader_mode(true, p_stencil_modes, defined_stencil_modes);
if (error != OK) {
return error;
}
}
tk = _get_token();
if (tk.type == TK_COMMA) {
//all good, do nothing
} else if (tk.type == TK_SEMICOLON) {
@ -11076,6 +11059,110 @@ 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<ModeInfo> &p_modes, HashMap<String, String> &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<StringName> &current_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<ModeInfo> modes = p_is_stencil ? ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)) : ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
for (const ModeInfo &info : modes) {
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 (const ModeInfo &info : p_modes) {
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;
@ -11226,7 +11313,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i
nodes = nullptr;
shader = alloc_node<ShaderNode>();
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) {
@ -11251,7 +11338,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<ShaderNode>();
_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.
@ -11349,6 +11436,67 @@ 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<ModeInfo> modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i));
for (const ModeInfo &info : modes) {
if (!info.options.is_empty()) {
bool found = false;
for (const StringName &option : info.options) {
if (shader->stencil_modes.has(String(info.name) + "_" + String(option))) {
found = true;
}
}
if (!found) {
for (const StringName &option : info.options) {
ScriptLanguage::CodeCompletionOption completion_option(String(info.name) + "_" + String(option), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
r_options->push_back(completion_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 (const ModeInfo &info : p_info.stencil_modes) {
if (!info.options.is_empty()) {
bool found = false;
for (const StringName &option : info.options) {
if (shader->stencil_modes.has(String(info.name) + "_" + String(option))) {
found = true;
}
}
if (!found) {
for (const StringName &option : info.options) {
ScriptLanguage::CodeCompletionOption completion_option(String(info.name) + "_" + String(option), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
r_options->push_back(completion_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;