|
@@ -96,6 +96,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
|
|
|
"FLOAT_CONSTANT",
|
|
|
"INT_CONSTANT",
|
|
|
"UINT_CONSTANT",
|
|
|
+ "STRING_CONSTANT",
|
|
|
"TYPE_VOID",
|
|
|
"TYPE_BOOL",
|
|
|
"TYPE_BVEC2",
|
|
@@ -212,6 +213,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
|
|
|
"HINT_ANISOTROPY_TEXTURE",
|
|
|
"HINT_SOURCE_COLOR",
|
|
|
"HINT_RANGE",
|
|
|
+ "HINT_ENUM",
|
|
|
"HINT_INSTANCE_INDEX",
|
|
|
"HINT_SCREEN_TEXTURE",
|
|
|
"HINT_NORMAL_ROUGHNESS_TEXTURE",
|
|
@@ -365,6 +367,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
|
|
|
|
|
|
{ TK_HINT_SOURCE_COLOR, "source_color", CF_UNSPECIFIED, {}, {} },
|
|
|
{ TK_HINT_RANGE, "hint_range", CF_UNSPECIFIED, {}, {} },
|
|
|
+ { TK_HINT_ENUM, "hint_enum", CF_UNSPECIFIED, {}, {} },
|
|
|
{ TK_HINT_INSTANCE_INDEX, "instance_index", CF_UNSPECIFIED, {}, {} },
|
|
|
|
|
|
// sampler hints
|
|
@@ -512,7 +515,54 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|
|
return _make_token(TK_OP_NOT);
|
|
|
|
|
|
} break;
|
|
|
- //case '"' //string - no strings in shader
|
|
|
+ case '"': {
|
|
|
+ String _content = "";
|
|
|
+ bool _previous_backslash = false;
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ bool _ended = false;
|
|
|
+ char32_t c = GETCHAR(0);
|
|
|
+ if (c == 0) {
|
|
|
+ return _make_token(TK_ERROR, "EOF reached before string termination.");
|
|
|
+ }
|
|
|
+ switch (c) {
|
|
|
+ case '"': {
|
|
|
+ if (_previous_backslash) {
|
|
|
+ _content += '"';
|
|
|
+ _previous_backslash = false;
|
|
|
+ } else {
|
|
|
+ _ended = true;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case '\\': {
|
|
|
+ if (_previous_backslash) {
|
|
|
+ _content += '\\';
|
|
|
+ }
|
|
|
+ _previous_backslash = !_previous_backslash;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case '\n': {
|
|
|
+ return _make_token(TK_ERROR, "Unexpected end of string.");
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ if (!_previous_backslash) {
|
|
|
+ _content += c;
|
|
|
+ } else {
|
|
|
+ return _make_token(TK_ERROR, "Only \\\" and \\\\ escape characters supported.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ char_idx++;
|
|
|
+ if (_ended) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return _make_token(TK_STRING_CONSTANT, _content);
|
|
|
+ } break;
|
|
|
//case '\'' //string - no strings in shader
|
|
|
case '{':
|
|
|
return _make_token(TK_CURLY_BRACKET_OPEN);
|
|
@@ -1120,6 +1170,9 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) {
|
|
|
case ShaderNode::Uniform::HINT_RANGE: {
|
|
|
result = "hint_range";
|
|
|
} break;
|
|
|
+ case ShaderNode::Uniform::HINT_ENUM: {
|
|
|
+ result = "hint_enum";
|
|
|
+ } break;
|
|
|
case ShaderNode::Uniform::HINT_SOURCE_COLOR: {
|
|
|
result = "source_color";
|
|
|
} break;
|
|
@@ -4139,6 +4192,11 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
|
|
|
if (p_uniform.array_size > 0) {
|
|
|
pi.type = Variant::PACKED_INT32_ARRAY;
|
|
|
// TODO: Handle range and encoding for for unsigned values.
|
|
|
+ } else if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_ENUM) {
|
|
|
+ pi.type = Variant::INT;
|
|
|
+ pi.hint = PROPERTY_HINT_ENUM;
|
|
|
+ String hint_string;
|
|
|
+ pi.hint_string = String(",").join(p_uniform.hint_enum_names);
|
|
|
} else {
|
|
|
pi.type = Variant::INT;
|
|
|
pi.hint = PROPERTY_HINT_RANGE;
|
|
@@ -8937,6 +8995,40 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
|
|
|
|
|
|
new_hint = ShaderNode::Uniform::HINT_RANGE;
|
|
|
} break;
|
|
|
+ case TK_HINT_ENUM: {
|
|
|
+ if (type != TYPE_INT) {
|
|
|
+ _set_error(vformat(RTR("Enum hint is for '%s' only."), "int"));
|
|
|
+ return ERR_PARSE_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ tk = _get_token();
|
|
|
+ if (tk.type != TK_PARENTHESIS_OPEN) {
|
|
|
+ _set_expected_after_error("(", "hint_enum");
|
|
|
+ return ERR_PARSE_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ tk = _get_token();
|
|
|
+
|
|
|
+ if (tk.type != TK_STRING_CONSTANT) {
|
|
|
+ _set_error(RTR("Expected a string constant."));
|
|
|
+ return ERR_PARSE_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ uniform.hint_enum_names.push_back(tk.text);
|
|
|
+
|
|
|
+ tk = _get_token();
|
|
|
+
|
|
|
+ if (tk.type == TK_PARENTHESIS_CLOSE) {
|
|
|
+ break;
|
|
|
+ } else if (tk.type != TK_COMMA) {
|
|
|
+ _set_error(RTR("Expected ',' or ')' after string constant."));
|
|
|
+ return ERR_PARSE_ERROR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ new_hint = ShaderNode::Uniform::HINT_ENUM;
|
|
|
+ } break;
|
|
|
case TK_HINT_INSTANCE_INDEX: {
|
|
|
if (custom_instance_index != -1) {
|
|
|
_set_error(vformat(RTR("Can only specify '%s' once."), "instance_index"));
|
|
@@ -9031,7 +9123,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
- if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || (new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE)) && !is_sampler_type(type)) {
|
|
|
+
|
|
|
+ bool is_sampler_hint = new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE && new_hint != ShaderNode::Uniform::HINT_ENUM;
|
|
|
+ if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || is_sampler_hint) && !is_sampler_type(type)) {
|
|
|
_set_error(RTR("This hint is only for sampler types."));
|
|
|
return ERR_PARSE_ERROR;
|
|
|
}
|
|
@@ -10730,15 +10824,21 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
|
|
|
}
|
|
|
} else if ((completion_base == DataType::TYPE_INT || completion_base == DataType::TYPE_FLOAT) && !completion_base_array) {
|
|
|
if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
|
|
|
- ScriptLanguage::CodeCompletionOption option("hint_range", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
|
|
+ Vector<String> options;
|
|
|
|
|
|
if (completion_base == DataType::TYPE_INT) {
|
|
|
- option.insert_text = "hint_range(0, 100, 1)";
|
|
|
+ options.push_back("hint_range(0, 100, 1)");
|
|
|
+ options.push_back("hint_enum(\"Zero\", \"One\", \"Two\")");
|
|
|
} else {
|
|
|
- option.insert_text = "hint_range(0.0, 1.0, 0.1)";
|
|
|
+ options.push_back("hint_range(0.0, 1.0, 0.1)");
|
|
|
}
|
|
|
|
|
|
- r_options->push_back(option);
|
|
|
+ for (const String &option_text : options) {
|
|
|
+ String hint_name = option_text.substr(0, option_text.find_char(char32_t('(')));
|
|
|
+ ScriptLanguage::CodeCompletionOption option(hint_name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
|
|
+ option.insert_text = option_text;
|
|
|
+ r_options->push_back(option);
|
|
|
+ }
|
|
|
}
|
|
|
} else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT))) {
|
|
|
Vector<String> options;
|