diff options
Diffstat (limited to 'servers/visual/shader_language.cpp')
-rw-r--r-- | servers/visual/shader_language.cpp | 1424 |
1 files changed, 1245 insertions, 179 deletions
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 98ccfcfc68..734abd7365 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -207,6 +207,14 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "HINT_BLACK_ALBEDO_TEXTURE", "HINT_COLOR", "HINT_RANGE", + "FILTER_NEAREST", + "FILTER_LINEAR", + "FILTER_NEAREST_MIPMAP", + "FILTER_LINEAR_MIPMAP", + "FILTER_NEAREST_MIPMAP_ANISO", + "FILTER_LINEAR_MIPMAP_ANISO", + "REPEAT_ENABLE", + "REPEAT_DISABLE", "SHADER_TYPE", "CURSOR", "ERROR", @@ -275,6 +283,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_INTERPOLATION_FLAT, "flat" }, { TK_INTERPOLATION_SMOOTH, "smooth" }, { TK_CONST, "const" }, + { TK_STRUCT, "struct" }, { TK_PRECISION_LOW, "lowp" }, { TK_PRECISION_MID, "mediump" }, { TK_PRECISION_HIGH, "highp" }, @@ -299,13 +308,26 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_HINT_WHITE_TEXTURE, "hint_white" }, { TK_HINT_BLACK_TEXTURE, "hint_black" }, { TK_HINT_NORMAL_TEXTURE, "hint_normal" }, + { TK_HINT_ROUGHNESS_NORMAL_TEXTURE, "hint_roughness_normal" }, + { TK_HINT_ROUGHNESS_R, "hint_roughness_r" }, + { TK_HINT_ROUGHNESS_G, "hint_roughness_g" }, + { TK_HINT_ROUGHNESS_B, "hint_roughness_b" }, + { TK_HINT_ROUGHNESS_A, "hint_roughness_a" }, + { TK_HINT_ROUGHNESS_GRAY, "hint_roughness_gray" }, { TK_HINT_ANISO_TEXTURE, "hint_aniso" }, { TK_HINT_ALBEDO_TEXTURE, "hint_albedo" }, { TK_HINT_BLACK_ALBEDO_TEXTURE, "hint_black_albedo" }, { TK_HINT_COLOR, "hint_color" }, { TK_HINT_RANGE, "hint_range" }, + { TK_FILTER_NEAREST, "filter_nearest" }, + { TK_FILTER_LINEAR, "filter_linear" }, + { TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap" }, + { TK_FILTER_LINEAR_MIPMAP, "filter_linear_mipmap" }, + { TK_FILTER_NEAREST_MIPMAP_ANISO, "filter_nearest_mipmap_aniso" }, + { TK_FILTER_LINEAR_MIPMAP_ANISO, "filter_linear_mipmap_aniso" }, + { TK_REPEAT_ENABLE, "repeat_enable" }, + { TK_REPEAT_DISABLE, "repeat_disable" }, { TK_SHADER_TYPE, "shader_type" }, - { TK_ERROR, NULL } }; @@ -841,6 +863,7 @@ String ShaderLanguage::get_datatype_name(DataType p_type) { case TYPE_ISAMPLER3D: return "isampler3D"; case TYPE_USAMPLER3D: return "usampler3D"; case TYPE_SAMPLERCUBE: return "samplerCube"; + case TYPE_STRUCT: return "struct"; } return ""; @@ -859,6 +882,7 @@ void ShaderLanguage::clear() { completion_block = NULL; completion_function = StringName(); completion_class = SubClassTag::TAG_GLOBAL; + completion_struct = StringName(); error_line = 0; tk_line = 1; @@ -872,13 +896,16 @@ void ShaderLanguage::clear() { } } -bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size) { +bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) { if (p_builtin_types.has(p_identifier)) { if (r_data_type) { *r_data_type = p_builtin_types[p_identifier].type; } + if (r_is_const) { + *r_is_const = p_builtin_types[p_identifier].constant; + } if (r_type) { *r_type = IDENTIFIER_BUILTIN_VAR; } @@ -903,6 +930,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String if (r_type) { *r_type = IDENTIFIER_LOCAL_VAR; } + if (r_struct_name) { + *r_struct_name = p_block->variables[p_identifier].struct_name; + } return true; } @@ -925,7 +955,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String if (r_type) { *r_type = IDENTIFIER_FUNCTION_ARGUMENT; } - + if (r_struct_name) { + *r_struct_name = function->arguments[i].type_str; + } return true; } } @@ -961,6 +993,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String if (r_type) { *r_type = IDENTIFIER_CONSTANT; } + if (r_struct_name) { + *r_struct_name = shader->constants[p_identifier].type_str; + } return true; } @@ -1207,7 +1242,11 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_ASSIGN: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); - valid = na == nb; + if (na == TYPE_STRUCT || nb == TYPE_STRUCT) { + valid = p_op->arguments[0]->get_datatype_name() == p_op->arguments[1]->get_datatype_name(); + } else { + valid = na == nb; + } ret_type = na; } break; case OP_ASSIGN_ADD: @@ -2075,11 +2114,12 @@ const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] { NULL, 0 } }; -bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type) { +bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false); Vector<DataType> args; + Vector<StringName> args2; ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false); @@ -2087,6 +2127,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p for (int i = 1; i < p_func->arguments.size(); i++) { args.push_back(p_func->arguments[i]->get_datatype()); + args2.push_back(p_func->arguments[i]->get_datatype_name()); } int argcount = args.size(); @@ -2266,7 +2307,10 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p bool fail = false; for (int j = 0; j < args.size(); j++) { - + if (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) { + fail = true; + break; + } if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { //all good, but it needs implicit conversion later } else if (args[j] != pfunc->arguments[j].type) { @@ -2296,8 +2340,13 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p p_func->arguments.write[k + 1] = conversion; } - if (r_ret_type) + if (r_ret_type) { *r_ret_type = pfunc->return_type; + if (pfunc->return_type == TYPE_STRUCT) { + *r_ret_type_str = pfunc->return_struct_name; + } + } + return true; } } @@ -2305,6 +2354,18 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p return false; } +bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const { + if (a->get_datatype() != b->get_datatype()) { + return false; + } + if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) { + if (a->get_datatype_name() != b->get_datatype_name()) { + return false; + } + } + return true; +} + bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg) { TkPos pos = _get_tkpos(); @@ -2550,6 +2611,8 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C // Texture types, likely not relevant here. break; } + case ShaderLanguage::TYPE_STRUCT: + break; case ShaderLanguage::TYPE_VOID: break; } @@ -2558,6 +2621,148 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C return Variant(); } +PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform &p_uniform) { + PropertyInfo pi; + switch (p_uniform.type) { + case ShaderLanguage::TYPE_VOID: pi.type = Variant::NIL; break; + case ShaderLanguage::TYPE_BOOL: pi.type = Variant::BOOL; break; + case ShaderLanguage::TYPE_BVEC2: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y"; + break; + case ShaderLanguage::TYPE_BVEC3: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z"; + break; + case ShaderLanguage::TYPE_BVEC4: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z,w"; + break; + case ShaderLanguage::TYPE_UINT: + case ShaderLanguage::TYPE_INT: { + pi.type = Variant::INT; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]); + } + + } break; + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC2: + case ShaderLanguage::TYPE_UVEC3: + case ShaderLanguage::TYPE_UVEC4: { + + pi.type = Variant::POOL_INT_ARRAY; + } break; + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::REAL; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + } + + } break; + case ShaderLanguage::TYPE_VEC2: pi.type = Variant::VECTOR2; break; + case ShaderLanguage::TYPE_VEC3: pi.type = Variant::VECTOR3; break; + case ShaderLanguage::TYPE_VEC4: { + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + pi.type = Variant::COLOR; + } else { + pi.type = Variant::PLANE; + } + } break; + case ShaderLanguage::TYPE_MAT2: pi.type = Variant::TRANSFORM2D; break; + case ShaderLanguage::TYPE_MAT3: pi.type = Variant::BASIS; break; + case ShaderLanguage::TYPE_MAT4: pi.type = Variant::TRANSFORM; break; + case ShaderLanguage::TYPE_SAMPLER2D: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture2D"; + } break; + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "TextureArray"; + } break; + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture3D"; + } break; + case ShaderLanguage::TYPE_SAMPLERCUBE: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "CubeMap"; + } break; + case ShaderLanguage::TYPE_STRUCT: { + // FIXME: Implement this. + } break; + } + return pi; +} + +uint32_t ShaderLanguage::get_type_size(DataType p_type) { + switch (p_type) { + case TYPE_VOID: + return 0; + case TYPE_BOOL: + case TYPE_INT: + case TYPE_UINT: + case TYPE_FLOAT: + return 4; + case TYPE_BVEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_VEC2: + return 8; + case TYPE_BVEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_VEC3: + return 12; + case TYPE_BVEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_VEC4: + return 16; + case TYPE_MAT2: + return 8; + case TYPE_MAT3: + return 12; + case TYPE_MAT4: + return 16; + case TYPE_SAMPLER2D: + case TYPE_ISAMPLER2D: + case TYPE_USAMPLER2D: + case TYPE_SAMPLER2DARRAY: + case TYPE_ISAMPLER2DARRAY: + case TYPE_USAMPLER2DARRAY: + case TYPE_SAMPLER3D: + case TYPE_ISAMPLER3D: + case TYPE_USAMPLER3D: + case TYPE_SAMPLERCUBE: + return 4; //not really, but useful for indices + case TYPE_STRUCT: + // FIXME: Implement. + return 0; + } + return 0; +} + void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { Set<String> kws; @@ -2744,6 +2949,13 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI } else if (p_node->type == Node::TYPE_MEMBER) { MemberNode *member = static_cast<MemberNode *>(p_node); + + if (member->has_swizzling_duplicates) { + if (r_message) + *r_message = RTR("Swizzling assignment contains duplicates."); + return false; + } + return _validate_assign(member->owner, p_builtin_types, r_message); } else if (p_node->type == Node::TYPE_VARIABLE) { @@ -2795,6 +3007,78 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI return false; } +bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { + for (int i = 0; shader->functions.size(); i++) { + if (shader->functions[i].name == p_name) { + + ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); + FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; + if (arg->tex_builtin_check) { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."); + return false; + } else if (arg->tex_argument_check) { + //was checked, verify that filter and repeat are the same + if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat) { + return true; + } else { + + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using textures that differ in either filter or repeat setting."); + return false; + } + } else { + + arg->tex_argument_check = true; + arg->tex_argument_filter = p_filter; + arg->tex_argument_repeat = p_repeat; + for (Map<StringName, Set<int> >::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { + for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_uniform_settings(E->key(), F->get(), p_filter, p_repeat)) { + return false; + } + } + } + return true; + } + } + } + ERR_FAIL_V(false); //bug? function not found +} +bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin) { + for (int i = 0; shader->functions.size(); i++) { + if (shader->functions[i].name == p_name) { + + ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); + FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; + if (arg->tex_argument_check) { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."); + return false; + } else if (arg->tex_builtin_check) { + //was checked, verify that the built-in is the same + if (arg->tex_builtin == p_builtin) { + return true; + } else { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using different built-ins. Only calling with the same built-in is supported."); + return false; + } + } else { + + arg->tex_builtin_check = true; + arg->tex_builtin = p_builtin; + + for (Map<StringName, Set<int> >::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { + for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_builtin_reference(E->key(), F->get(), p_builtin)) { + return false; + } + } + } + return true; + } + } + } + ERR_FAIL_V(false); //bug? function not found +} + ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { Vector<Expression> expression; @@ -2808,6 +3092,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons Token tk = _get_token(); TkPos pos = _get_tkpos(); + bool is_const = false; + if (tk.type == TK_PARENTHESIS_OPEN) { //handle subexpression @@ -2903,7 +3189,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (!ok) return NULL; - if (!_validate_function_call(p_block, func, &func->return_cache)) { + if (!_validate_function_call(p_block, func, &func->return_cache, &func->struct_name)) { _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); return NULL; } @@ -2916,66 +3202,371 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons StringName identifier; + StructNode *pstruct = NULL; + bool struct_init = false; + _get_completable_identifier(p_block, COMPLETION_IDENTIFIER, identifier); + if (shader->structs.has(identifier)) { + pstruct = shader->structs[identifier].shader_struct; + struct_init = true; + } + tk = _get_token(); if (tk.type == TK_PARENTHESIS_OPEN) { - //a function - const StringName &name = identifier; - OperatorNode *func = alloc_node<OperatorNode>(); - func->op = OP_CALL; - VariableNode *funcname = alloc_node<VariableNode>(); - funcname->name = name; - func->arguments.push_back(funcname); + if (struct_init) { //a struct constructor + + const StringName &name = identifier; + + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_STRUCT; + func->struct_name = name; + func->return_cache = TYPE_STRUCT; + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = name; + func->arguments.push_back(funcname); + + for (int i = 0; i < pstruct->members.size(); i++) { + Node *nexpr; + + if (pstruct->members[i]->array_size != 0) { + + DataType type = pstruct->members[i]->get_datatype(); + String struct_name = pstruct->members[i]->struct_name; + int array_size = pstruct->members[i]->array_size; + + DataType type2; + String struct_name2 = ""; + int array_size2 = 0; + + bool auto_size = false; + + tk = _get_token(); + + if (tk.type == TK_CURLY_BRACKET_OPEN) { + auto_size = true; + } else { + + if (shader->structs.has(tk.text)) { + type2 = TYPE_STRUCT; + struct_name2 = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); + return NULL; + } + type2 = get_token_datatype(tk.type); + } + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos2 = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + array_size2 = array_size; + tk = _get_token(); + } else { + _set_tkpos(pos2); + + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return NULL; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size2 = cnode->values[0].sint; + if (array_size2 <= 0) { + _set_error("Expected single integer constant > 0"); + return NULL; + } + } else { + _set_error("Expected single integer constant > 0"); + return NULL; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return NULL; + } else { + tk = _get_token(); + } + } + } else { + _set_error("Expected '['"); + return NULL; + } + + if (type != type2 || struct_name != struct_name2 || array_size != array_size2) { + String error_str = "Cannot convert from '"; + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + error_str += get_datatype_name(type2); + } + error_str += "["; + error_str += itos(array_size2); + error_str += "]'"; + error_str += " to '"; + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(array_size); + error_str += "]'"; + _set_error(error_str); + return NULL; + } + } + + ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); + an->datatype = type; + an->struct_name = struct_name; + + if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization + while (true) { + + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) { + return NULL; + } - int carg = -1; + if (type != n->get_datatype() || struct_name != n->get_datatype_name()) { + _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (type == TYPE_STRUCT ? struct_name : get_datatype_name(type)) + "'"); + return NULL; + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { + an->initializer.push_back(n); + continue; + } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { + an->initializer.push_back(n); + break; + } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { + an->initializer.push_back(n); + break; + } else { + if (auto_size) + _set_error("Expected '}' or ','"); + else + _set_error("Expected ')' or ','"); + return NULL; + } + } + if (an->initializer.size() != array_size) { + _set_error("Array size mismatch"); + return NULL; + } + } else { + _set_error("Expected array initialization!"); + return NULL; + } - bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + nexpr = an; + } else { + nexpr = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!nexpr) { + return NULL; + } + Node *node = pstruct->members[i]; + if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) { + String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype()); + String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype()); + _set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'"); + return NULL; + } + } - // Check if block has a variable with the same name as function to prevent shader crash. - ShaderLanguage::BlockNode *bnode = p_block; - while (bnode) { - if (bnode->variables.has(name)) { - _set_error("Expected function name"); + if (i + 1 < pstruct->members.size()) { + tk = _get_token(); + if (tk.type != TK_COMMA) { + _set_error("Expected ','"); + return NULL; + } + } + func->arguments.push_back(nexpr); + } + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); return NULL; } - bnode = bnode->parent_block; - } - //test if function was parsed first - for (int i = 0; i < shader->functions.size(); i++) { - if (shader->functions[i].name == name) { - //add to current function as dependency - for (int j = 0; j < shader->functions.size(); j++) { - if (shader->functions[j].name == current_function) { - shader->functions.write[j].uses_function.insert(name); - break; + expr = func; + + } else { //a function + + const StringName &name = identifier; + + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_CALL; + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = name; + func->arguments.push_back(funcname); + + int carg = -1; + + bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + + // Check if block has a variable with the same name as function to prevent shader crash. + ShaderLanguage::BlockNode *bnode = p_block; + while (bnode) { + if (bnode->variables.has(name)) { + _set_error("Expected function name"); + return NULL; + } + bnode = bnode->parent_block; + } + + //test if function was parsed first + int function_index = -1; + for (int i = 0; i < shader->functions.size(); i++) { + if (shader->functions[i].name == name) { + //add to current function as dependency + for (int j = 0; j < shader->functions.size(); j++) { + if (shader->functions[j].name == current_function) { + shader->functions.write[j].uses_function.insert(name); + break; + } } + + //see if texture arguments must connect + function_index = i; + break; } - break; } - } - if (carg >= 0) { - completion_type = COMPLETION_CALL_ARGUMENTS; - completion_line = tk_line; - completion_block = p_block; - completion_function = funcname->name; - completion_argument = carg; - } + if (carg >= 0) { + completion_type = COMPLETION_CALL_ARGUMENTS; + completion_line = tk_line; + completion_block = p_block; + completion_function = funcname->name; + completion_argument = carg; + } - if (!ok) - return NULL; + if (!ok) + return NULL; - if (!_validate_function_call(p_block, func, &func->return_cache)) { - _set_error("No matching function found for: '" + String(funcname->name) + "'"); - return NULL; - } - completion_class = TAG_GLOBAL; // reset sub-class + if (!_validate_function_call(p_block, func, &func->return_cache, &func->struct_name)) { + _set_error("No matching function found for: '" + String(funcname->name) + "'"); + return NULL; + } + completion_class = TAG_GLOBAL; // reset sub-class + if (function_index >= 0) { + //connect texture arguments, so we can cache in the + //argument what type of filter and repeat to use + + FunctionNode *call_function = shader->functions[function_index].function; + if (call_function) { - expr = func; + //get current base function + FunctionNode *base_function = NULL; + { + BlockNode *b = p_block; + + while (b) { + + if (b->parent_function) { + base_function = b->parent_function; + break; + } else { + b = b->parent_block; + } + } + } + ERR_FAIL_COND_V(!base_function, NULL); //bug, wtf + + for (int i = 0; i < call_function->arguments.size(); i++) { + int argidx = i + 1; + if (argidx < func->arguments.size()) { + if (call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT) { + bool error = false; + Node *n = func->arguments[argidx]; + if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR) { + error = true; + } else if (n->type == Node::TYPE_ARRAY) { + ArrayNode *an = static_cast<ArrayNode *>(n); + if (an->call_expression != NULL) { + error = true; + } + } else if (n->type == Node::TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(n); + if (vn->is_const) { + error = true; + } else { + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + error = true; + } else { + if (p_builtin_types.has(varname)) { + BuiltInInfo info = p_builtin_types[varname]; + if (info.constant) { + error = true; + } + } + } + } + } else if (n->type == Node::TYPE_MEMBER) { + MemberNode *mn = static_cast<MemberNode *>(n); + if (mn->basetype_const) { + error = true; + } + } + if (error) { + _set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier))); + return NULL; + } + } + if (is_sampler_type(call_function->arguments[i].type)) { + //let's see where our argument comes from + Node *n = func->arguments[argidx]; + ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable + VariableNode *vn = static_cast<VariableNode *>(n); + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + //being sampler, this either comes from a uniform + ShaderNode::Uniform *u = &shader->uniforms[varname]; + ERR_CONTINUE(u->type != call_function->arguments[i].type); //this should have been validated previously + //propagate + if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) { + return NULL; + } + } else if (p_builtin_types.has(varname)) { + //a built-in + if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) { + return NULL; + } + } else { + //or this comes from an argument, but nothing else can be a sampler + bool found = false; + for (int j = 0; j < base_function->arguments.size(); j++) { + if (base_function->arguments[j].name == varname) { + if (!base_function->arguments[j].tex_argument_connect.has(call_function->name)) { + base_function->arguments.write[j].tex_argument_connect[call_function->name] = Set<int>(); + } + base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i); + found = true; + break; + } + } + ERR_CONTINUE(!found); + } + } + } else { + break; + } + } + } + } + expr = func; + } } else { //an identifier @@ -2983,8 +3574,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons DataType data_type; IdentifierType ident_type; - bool is_const = false; int array_size = 0; + StringName struct_name; if (p_block && p_block->block_tag != SubClassTag::TAG_GLOBAL) { int idx = 0; @@ -3003,7 +3594,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } else { - if (!_find_identifier(p_block, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size)) { + if (!_find_identifier(p_block, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) { _set_error("Unknown identifier in expression: " + String(identifier)); return NULL; } @@ -3067,6 +3658,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ArrayNode *arrname = alloc_node<ArrayNode>(); arrname->name = identifier; arrname->datatype_cache = data_type; + arrname->struct_name = struct_name; arrname->index_expression = index_expression; arrname->call_expression = call_expression; arrname->is_const = is_const; @@ -3078,10 +3670,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons varname->name = identifier; varname->datatype_cache = data_type; varname->is_const = is_const; + varname->struct_name = struct_name; expr = varname; } } - } else if (tk.type == TK_OP_ADD) { continue; //this one does nothing } else if (tk.type == TK_OP_SUB || tk.type == TK_OP_NOT || tk.type == TK_OP_BIT_INVERT || tk.type == TK_OP_INCREMENT || tk.type == TK_OP_DECREMENT) { @@ -3100,7 +3692,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.push_back(e); continue; - } else { _set_error("Expected expression, found: " + get_token_text(tk)); return NULL; @@ -3119,23 +3710,60 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (tk.type == TK_CURSOR) { //do nothing + } else if (tk.type == TK_IDENTIFIER) { + } else if (tk.type == TK_PERIOD) { + DataType dt = expr->get_datatype(); + String st = expr->get_datatype_name(); + StringName identifier; - if (_get_completable_identifier(p_block, COMPLETION_INDEX, identifier)) { - completion_base = expr->get_datatype(); + if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) { + if (dt == TYPE_STRUCT) { + completion_struct = st; + } else { + completion_base = dt; + } } if (identifier == StringName()) { _set_error("Expected identifier as member"); return NULL; } - DataType dt = expr->get_datatype(); String ident = identifier; bool ok = true; + bool repeated = false; DataType member_type = TYPE_VOID; + StringName member_struct_name = ""; + int array_size = 0; + + Set<char> position_symbols; + Set<char> color_symbols; + Set<char> texture_symbols; + + bool mix_error = false; + switch (dt) { + case TYPE_STRUCT: { + ok = false; + String member_name = String(ident.ptr()); + if (shader->structs.has(st)) { + StructNode *n = shader->structs[st].shader_struct; + for (List<MemberNode *>::Element *E = n->members.front(); E; E = E->next()) { + if (String(E->get()->name) == member_name) { + member_type = E->get()->datatype; + array_size = E->get()->array_size; + if (member_type == TYPE_STRUCT) { + member_struct_name = E->get()->struct_name; + } + ok = true; + break; + } + } + } + + } break; case TYPE_BVEC2: case TYPE_IVEC2: case TYPE_UVEC2: @@ -3161,8 +3789,39 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons switch (c[i]) { case 'r': case 'g': + if (position_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!color_symbols.has(c[i])) { + color_symbols.insert(c[i]); + } else { + repeated = true; + } + break; case 'x': case 'y': + if (color_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!position_symbols.has(c[i])) { + position_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 's': + case 't': + if (color_symbols.size() > 0 || position_symbols.size() > 0) { + mix_error = true; + break; + } + if (!texture_symbols.has(c[i])) { + texture_symbols.insert(c[i]); + } else { + repeated = true; + } break; default: ok = false; @@ -3197,9 +3856,41 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case 'r': case 'g': case 'b': + if (position_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!color_symbols.has(c[i])) { + color_symbols.insert(c[i]); + } else { + repeated = true; + } + break; case 'x': case 'y': case 'z': + if (color_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!position_symbols.has(c[i])) { + position_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 's': + case 't': + case 'p': + if (color_symbols.size() > 0 || position_symbols.size() > 0) { + mix_error = true; + break; + } + if (!texture_symbols.has(c[i])) { + texture_symbols.insert(c[i]); + } else { + repeated = true; + } break; default: ok = false; @@ -3235,10 +3926,43 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case 'g': case 'b': case 'a': + if (position_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!color_symbols.has(c[i])) { + color_symbols.insert(c[i]); + } else { + repeated = true; + } + break; case 'x': case 'y': case 'z': case 'w': + if (color_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!position_symbols.has(c[i])) { + position_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 's': + case 't': + case 'p': + case 'q': + if (color_symbols.size() > 0 || position_symbols.size() > 0) { + mix_error = true; + break; + } + if (!texture_symbols.has(c[i])) { + texture_symbols.insert(c[i]); + } else { + repeated = true; + } break; default: ok = false; @@ -3253,17 +3977,70 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } - if (!ok) { + if (mix_error) { + _set_error("Cannot combine symbols from different sets in expression ." + ident); + return NULL; + } - _set_error("Invalid member for " + get_datatype_name(dt) + " expression: ." + ident); + if (!ok) { + _set_error("Invalid member for " + (dt == TYPE_STRUCT ? st : get_datatype_name(dt)) + " expression: ." + ident); return NULL; } MemberNode *mn = alloc_node<MemberNode>(); mn->basetype = dt; + mn->basetype_const = is_const; mn->datatype = member_type; + mn->base_struct_name = st; + mn->struct_name = member_struct_name; + mn->array_size = array_size; mn->name = ident; mn->owner = expr; + mn->has_swizzling_duplicates = repeated; + + if (array_size > 0) { + + tk = _get_token(); + if (tk.type == TK_PERIOD) { + _set_error("Nested array length() is not yet implemented"); + return NULL; + } else if (tk.type == TK_BRACKET_OPEN) { + + Node *index_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!index_expression) + return NULL; + + if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { + _set_error("Only integer expressions are allowed for indexing"); + return NULL; + } + + if (index_expression->type == Node::TYPE_CONSTANT) { + ConstantNode *cnode = (ConstantNode *)index_expression; + if (cnode) { + if (!cnode->values.empty()) { + int value = cnode->values[0].sint; + if (value < 0 || value >= array_size) { + _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); + return NULL; + } + } + } + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return NULL; + } + mn->index_expression = index_expression; + + } else { + _set_error("Expected '[' or '.'"); + return NULL; + } + } + expr = mn; //todo @@ -3358,7 +4135,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } break; default: { - _set_error("Object of type '" + get_datatype_name(expr->get_datatype()) + "' can't be indexed"); + _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); return NULL; } } @@ -3681,7 +4458,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) at += " and "; - at += get_datatype_name(op->arguments[i]->get_datatype()); + if (op->arguments[i]->get_datatype() == TYPE_STRUCT) { + at += op->arguments[i]->get_datatype_name(); + } else { + at += get_datatype_name(op->arguments[i]->get_datatype()); + } } _set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at); return NULL; @@ -3836,6 +4617,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } } + bool is_struct = shader->structs.has(tk.text); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block if (p_just_one) { _set_error("Unexpected '}'"); @@ -3844,31 +4627,50 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui return OK; - } else if (tk.type == TK_CONST || is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type)) { + } else if (tk.type == TK_CONST || is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type) || is_struct) { + String struct_name = ""; + if (is_struct) { + struct_name = tk.text; + } bool is_const = false; if (tk.type == TK_CONST) { is_const = true; tk = _get_token(); + + if (!is_struct) { + is_struct = shader->structs.has(tk.text); // check again. + struct_name = tk.text; + } } DataPrecision precision = PRECISION_DEFAULT; if (is_token_precision(tk.type)) { precision = get_token_precision(tk.type); tk = _get_token(); + + if (!is_struct) { + is_struct = shader->structs.has(tk.text); // check again. + } + if (is_struct && precision != PRECISION_DEFAULT) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } if (!is_token_nonvoid_datatype(tk.type)) { _set_error("Expected datatype after precision"); return ERR_PARSE_ERROR; } } - if (!is_token_variable_datatype(tk.type)) { - _set_error("Invalid data type for variable (samplers not allowed)"); - return ERR_PARSE_ERROR; + if (!is_struct) { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for variable (samplers not allowed)"); + return ERR_PARSE_ERROR; + } } - DataType type = get_token_datatype(tk.type); + DataType type = is_struct ? TYPE_STRUCT : get_token_datatype(tk.type); if (_validate_datatype(type) != OK) { return ERR_PARSE_ERROR; @@ -3900,6 +4702,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui var.line = tk_line; var.array_size = 0; var.is_const = is_const; + var.struct_name = struct_name; tk = _get_token(); @@ -3912,7 +4715,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } ArrayDeclarationNode *node = alloc_node<ArrayDeclarationNode>(); - node->datatype = type; + if (is_struct) { + node->struct_name = struct_name; + node->datatype = TYPE_STRUCT; + } else { + node->datatype = type; + } node->precision = precision; node->is_const = is_const; vardecl = (Node *)node; @@ -3967,16 +4775,29 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui if (is_token_precision(tk.type)) { precision2 = get_token_precision(tk.type); tk = _get_token(); + if (shader->structs.has(tk.text)) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } if (!is_token_nonvoid_datatype(tk.type)) { _set_error("Expected datatype after precision"); return ERR_PARSE_ERROR; } } - if (!is_token_variable_datatype(tk.type)) { - _set_error("Invalid data type for array"); - return ERR_PARSE_ERROR; + + DataType type2; + String struct_name2 = ""; + + if (shader->structs.has(tk.text)) { + type2 = TYPE_STRUCT; + struct_name2 = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); + return ERR_PARSE_ERROR; + } + type2 = get_token_datatype(tk.type); } - DataType type2 = get_token_datatype(tk.type); int array_size2 = 0; @@ -4021,13 +4842,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui return ERR_PARSE_ERROR; } - if (precision != precision2 || type != type2 || var.array_size != array_size2) { + if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { String error_str = "Cannot convert from '"; if (precision2 != PRECISION_DEFAULT) { error_str += get_precision_name(precision2); error_str += " "; } - error_str += get_datatype_name(type2); + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + error_str += get_datatype_name(type2); + } error_str += "["; error_str += itos(array_size2); error_str += "]'"; @@ -4036,7 +4861,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui error_str += get_precision_name(precision); error_str += " "; } - error_str += get_datatype_name(type); + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } error_str += "["; error_str += itos(var.array_size); error_str += "]'"; @@ -4074,8 +4903,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui return ERR_PARSE_ERROR; } - if (var.type != n->get_datatype()) { - _set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(var.type) + "'"); + if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) { + _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'"); return ERR_PARSE_ERROR; } @@ -4121,7 +4950,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } else if (tk.type == TK_OP_ASSIGN) { VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>(); - node->datatype = type; + if (is_struct) { + node->struct_name = struct_name; + node->datatype = TYPE_STRUCT; + } else { + node->datatype = type; + } node->precision = precision; node->is_const = is_const; vardecl = (Node *)node; @@ -4140,8 +4974,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } decl.initializer = n; - if (var.type != n->get_datatype()) { - _set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(var.type) + "'"); + if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) { + _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'"); return ERR_PARSE_ERROR; } tk = _get_token(); @@ -4153,7 +4987,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>(); - node->datatype = type; + if (is_struct) { + node->struct_name = struct_name; + node->datatype = TYPE_STRUCT; + } else { + node->datatype = type; + } node->precision = precision; vardecl = (Node *)node; @@ -4700,6 +5539,18 @@ String ShaderLanguage::_get_shader_type_list(const Set<String> &p_shader_types) return valid_types; } +String ShaderLanguage::_get_qualifier_str(ArgumentQualifier p_qualifier) const { + switch (p_qualifier) { + case ArgumentQualifier::ARGUMENT_QUALIFIER_IN: + return "in"; + case ArgumentQualifier::ARGUMENT_QUALIFIER_OUT: + return "out"; + case ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT: + return "inout"; + } + return ""; +} + Error ShaderLanguage::_validate_datatype(DataType p_type) { if (VisualServer::get_singleton()->is_low_end()) { bool invalid_type = false; @@ -4803,6 +5654,129 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } } } break; + case TK_STRUCT: { + ShaderNode::Struct st; + DataType type; + + tk = _get_token(); + if (tk.type == TK_IDENTIFIER) { + st.name = tk.text; + tk = _get_token(); + if (tk.type != TK_CURLY_BRACKET_OPEN) { + _set_error("Expected '{'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected struct identifier!"); + return ERR_PARSE_ERROR; + } + + StructNode *st_node = alloc_node<StructNode>(); + st.shader_struct = st_node; + + int member_count = 0; + + while (true) { // variables list + tk = _get_token(); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + StringName struct_name = ""; + bool struct_dt = false; + bool use_precision = false; + DataPrecision precision = DataPrecision::PRECISION_DEFAULT; + + if (tk.type == TK_STRUCT) { + _set_error("nested structs are not allowed!"); + return ERR_PARSE_ERROR; + } + + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + use_precision = true; + tk = _get_token(); + } + + if (shader->structs.has(tk.text)) { + struct_name = tk.text; + struct_dt = true; + if (use_precision) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + } + + if (!is_token_datatype(tk.type) && !struct_dt) { + _set_error("Expected datatype."); + return ERR_PARSE_ERROR; + } else { + type = struct_dt ? TYPE_STRUCT : get_token_datatype(tk.type); + + if (is_sampler_type(type)) { + _set_error("sampler datatype not allowed here"); + return ERR_PARSE_ERROR; + } else if (type == TYPE_VOID) { + _set_error("void datatype not allowed here"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier!"); + return ERR_PARSE_ERROR; + } + + MemberNode *member = alloc_node<MemberNode>(); + member->precision = precision; + member->datatype = type; + member->struct_name = struct_name; + member->name = tk.text; + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + tk = _get_token(); + if (tk.type == TK_INT_CONSTANT && tk.constant > 0) { + member->array_size = (int)tk.constant; + + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + } + st_node->members.push_back(member); + + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ']' or ';'"); + return ERR_PARSE_ERROR; + } + member_count++; + } + } + if (member_count == 0) { + _set_error("Empty structs are not allowed!"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + shader->structs[st.name] = st; + shader->vstructs.push_back(st); // struct's order is important! + + } break; case TK_UNIFORM: case TK_VARYING: { @@ -4881,116 +5855,145 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (tk.type == TK_COLON) { //hint - - tk = _get_token(); - if (tk.type == TK_HINT_WHITE_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_WHITE; - } else if (tk.type == TK_HINT_BLACK_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_BLACK; - } else if (tk.type == TK_HINT_NORMAL_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_NORMAL; - } else if (tk.type == TK_HINT_ANISO_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_ANISO; - } else if (tk.type == TK_HINT_ALBEDO_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_ALBEDO; - } else if (tk.type == TK_HINT_BLACK_ALBEDO_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_BLACK_ALBEDO; - } else if (tk.type == TK_HINT_COLOR) { - if (type != TYPE_VEC4) { - _set_error("Color hint is for vec4 only"); - return ERR_PARSE_ERROR; - } - uniform2.hint = ShaderNode::Uniform::HINT_COLOR; - } else if (tk.type == TK_HINT_RANGE) { - - uniform2.hint = ShaderNode::Uniform::HINT_RANGE; - if (type != TYPE_FLOAT && type != TYPE_INT) { - _set_error("Range hint is for float and int only"); - return ERR_PARSE_ERROR; - } - - tk = _get_token(); - if (tk.type != TK_PARENTHESIS_OPEN) { - _set_error("Expected '(' after hint_range"); - return ERR_PARSE_ERROR; - } - + do { tk = _get_token(); + if (tk.type == TK_HINT_WHITE_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_WHITE; + } else if (tk.type == TK_HINT_BLACK_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_BLACK; + } else if (tk.type == TK_HINT_NORMAL_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_NORMAL; + } else if (tk.type == TK_HINT_ROUGHNESS_NORMAL_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL; + } else if (tk.type == TK_HINT_ROUGHNESS_R) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_R; + } else if (tk.type == TK_HINT_ROUGHNESS_G) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_G; + } else if (tk.type == TK_HINT_ROUGHNESS_B) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_B; + } else if (tk.type == TK_HINT_ROUGHNESS_A) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_A; + } else if (tk.type == TK_HINT_ROUGHNESS_GRAY) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_GRAY; + } else if (tk.type == TK_HINT_ANISO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ANISO; + } else if (tk.type == TK_HINT_ALBEDO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ALBEDO; + } else if (tk.type == TK_HINT_BLACK_ALBEDO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_BLACK_ALBEDO; + } else if (tk.type == TK_HINT_COLOR) { + if (type != TYPE_VEC4) { + _set_error("Color hint is for vec4 only"); + return ERR_PARSE_ERROR; + } + uniform2.hint = ShaderNode::Uniform::HINT_COLOR; + } else if (tk.type == TK_HINT_RANGE) { - float sign = 1.0; + uniform2.hint = ShaderNode::Uniform::HINT_RANGE; + if (type != TYPE_FLOAT && type != TYPE_INT) { + _set_error("Range hint is for float and int only"); + return ERR_PARSE_ERROR; + } - if (tk.type == TK_OP_SUB) { - sign = -1.0; tk = _get_token(); - } - - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { - _set_error("Expected integer constant"); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after hint_range"); + return ERR_PARSE_ERROR; + } - uniform2.hint_range[0] = tk.constant; - uniform2.hint_range[0] *= sign; + tk = _get_token(); - tk = _get_token(); + float sign = 1.0; - if (tk.type != TK_COMMA) { - _set_error("Expected ',' after integer constant"); - return ERR_PARSE_ERROR; - } + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } - tk = _get_token(); + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant"); + return ERR_PARSE_ERROR; + } - sign = 1.0; + uniform2.hint_range[0] = tk.constant; + uniform2.hint_range[0] *= sign; - if (tk.type == TK_OP_SUB) { - sign = -1.0; tk = _get_token(); - } - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { - _set_error("Expected integer constant after ','"); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_COMMA) { + _set_error("Expected ',' after integer constant"); + return ERR_PARSE_ERROR; + } - uniform2.hint_range[1] = tk.constant; - uniform2.hint_range[1] *= sign; + tk = _get_token(); - tk = _get_token(); + sign = 1.0; - if (tk.type == TK_COMMA) { - tk = _get_token(); + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } - uniform2.hint_range[2] = tk.constant; + uniform2.hint_range[1] = tk.constant; + uniform2.hint_range[1] *= sign; + tk = _get_token(); - } else { - if (type == TYPE_INT) { - uniform2.hint_range[2] = 1; + + if (tk.type == TK_COMMA) { + tk = _get_token(); + + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant after ','"); + return ERR_PARSE_ERROR; + } + + uniform2.hint_range[2] = tk.constant; + tk = _get_token(); } else { - uniform2.hint_range[2] = 0.001; + if (type == TYPE_INT) { + uniform2.hint_range[2] = 1; + } else { + uniform2.hint_range[2] = 0.001; + } + } + + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ','"); + return ERR_PARSE_ERROR; } + } else if (tk.type == TK_FILTER_LINEAR) { + uniform2.filter = FILTER_LINEAR; + } else if (tk.type == TK_FILTER_NEAREST) { + uniform2.filter = FILTER_NEAREST; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP) { + uniform2.filter = FILTER_NEAREST_MIPMAP; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP) { + uniform2.filter = FILTER_LINEAR_MIPMAP; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP_ANISO) { + uniform2.filter = FILTER_NEAREST_MIPMAP_ANISO; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP_ANISO) { + uniform2.filter = FILTER_LINEAR_MIPMAP_ANISO; + } else if (tk.type == TK_REPEAT_DISABLE) { + uniform2.repeat = REPEAT_DISABLE; + } else if (tk.type == TK_REPEAT_ENABLE) { + uniform2.repeat = REPEAT_ENABLE; + } else { + _set_error("Expected valid type hint after ':'."); } - if (tk.type != TK_PARENTHESIS_CLOSE) { - _set_error("Expected ','"); + if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) { + _set_error("This hint is only for sampler types"); return ERR_PARSE_ERROR; } - } else { - _set_error("Expected valid type hint after ':'."); - } - - if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) { - _set_error("This hint is only for sampler types"); - return ERR_PARSE_ERROR; - } + tk = _get_token(); - tk = _get_token(); + } while (tk.type == TK_COMMA); } if (tk.type == TK_OP_ASSIGN) { @@ -5063,6 +6066,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct //function or constant variable bool is_constant = false; + bool is_struct = false; + StringName struct_name; DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; @@ -5077,18 +6082,31 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); } - if (!is_token_datatype(tk.type)) { - _set_error("Expected constant, function, uniform or varying "); - return ERR_PARSE_ERROR; - } + if (shader->structs.has(tk.text)) { + if (precision != PRECISION_DEFAULT) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + is_struct = true; + struct_name = tk.text; + } else { - if (!is_token_variable_datatype(tk.type)) { - _set_error("Invalid data type for constants or function return (samplers not allowed)"); - return ERR_PARSE_ERROR; - } + if (!is_token_datatype(tk.type)) { + _set_error("Expected constant, function, uniform or varying"); + return ERR_PARSE_ERROR; + } - type = get_token_datatype(tk.type); + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for constants or function return (samplers not allowed)"); + return ERR_PARSE_ERROR; + } + } + if (is_struct) { + type = TYPE_STRUCT; + } else { + type = get_token_datatype(tk.type); + } TkPos prev_pos = _get_tkpos(); tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { @@ -5125,7 +6143,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct while (true) { ShaderNode::Constant constant; - constant.type = type; + constant.type = is_struct ? TYPE_STRUCT : type; + constant.type_str = struct_name; constant.precision = precision; constant.initializer = NULL; @@ -5140,7 +6159,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); if (!expr) return ERR_PARSE_ERROR; - if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { _set_error("Expected constant expression after '='"); return ERR_PARSE_ERROR; @@ -5148,7 +6166,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct constant.initializer = static_cast<ConstantNode *>(expr); - if (type != expr->get_datatype()) { + if (is_struct) { + if (expr->get_datatype_name() != struct_name) { + _set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + struct_name + "'"); + return ERR_PARSE_ERROR; + } + } else if (type != expr->get_datatype()) { _set_error("Invalid assignment of '" + get_datatype_name(expr->get_datatype()) + "' to '" + get_datatype_name(type) + "'"); return ERR_PARSE_ERROR; } @@ -5208,6 +6231,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct func_node->name = name; func_node->return_type = type; + func_node->return_struct_name = struct_name; func_node->return_precision = precision; if (p_functions.has(name)) { @@ -5239,27 +6263,50 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct DataType ptype; StringName pname; + StringName param_struct_name; DataPrecision pprecision = PRECISION_DEFAULT; + bool use_precision = false; if (is_token_precision(tk.type)) { pprecision = get_token_precision(tk.type); tk = _get_token(); + use_precision = true; } - if (!is_token_datatype(tk.type)) { + is_struct = false; + + if (shader->structs.has(tk.text)) { + is_struct = true; + param_struct_name = tk.text; + if (use_precision) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + } + + if (!is_struct && !is_token_datatype(tk.type)) { _set_error("Expected a valid datatype for argument"); return ERR_PARSE_ERROR; } - ptype = get_token_datatype(tk.type); - - if (_validate_datatype(ptype) != OK) { - return ERR_PARSE_ERROR; + if (qualifier == ARGUMENT_QUALIFIER_OUT || qualifier == ARGUMENT_QUALIFIER_INOUT) { + if (is_sampler_type(get_token_datatype(tk.type))) { + _set_error("Opaque types cannot be output parameters."); + return ERR_PARSE_ERROR; + } } - if (ptype == TYPE_VOID) { - _set_error("void not allowed in argument"); - return ERR_PARSE_ERROR; + if (is_struct) { + ptype = TYPE_STRUCT; + } else { + ptype = get_token_datatype(tk.type); + if (_validate_datatype(ptype) != OK) { + return ERR_PARSE_ERROR; + } + if (ptype == TYPE_VOID) { + _set_error("void not allowed in argument"); + return ERR_PARSE_ERROR; + } } tk = _get_token(); @@ -5291,8 +6338,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct FunctionNode::Argument arg; arg.type = ptype; arg.name = pname; + arg.type_str = param_struct_name; arg.precision = pprecision; arg.qualifier = qualifier; + arg.tex_argument_check = false; + arg.tex_builtin_check = false; + arg.tex_argument_filter = FILTER_DEFAULT; + arg.tex_argument_repeat = REPEAT_DEFAULT; func_node->arguments.push_back(arg); @@ -5547,6 +6599,18 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct return OK; } break; + case COMPLETION_STRUCT: { + + if (shader->structs.has(completion_struct)) { + StructNode *node = shader->structs[completion_struct].shader_struct; + for (int i = 0; i < node->members.size(); i++) { + ScriptCodeCompletionOption option(node->members[i]->name, ScriptCodeCompletionOption::KIND_MEMBER); + r_options->push_back(option); + } + } + + return OK; + } break; case COMPLETION_MAIN_FUNCTION: { for (const Map<StringName, FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) { @@ -5760,6 +6824,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct const char colv[4] = { 'r', 'g', 'b', 'a' }; const char coordv[4] = { 'x', 'y', 'z', 'w' }; + const char coordt[4] = { 's', 't', 'p', 'q' }; int limit = 0; @@ -5797,6 +6862,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct for (int i = 0; i < limit; i++) { r_options->push_back(ScriptCodeCompletionOption(String::chr(colv[i]), ScriptCodeCompletionOption::KIND_PLAIN_TEXT)); r_options->push_back(ScriptCodeCompletionOption(String::chr(coordv[i]), ScriptCodeCompletionOption::KIND_PLAIN_TEXT)); + r_options->push_back(ScriptCodeCompletionOption(String::chr(coordt[i]), ScriptCodeCompletionOption::KIND_PLAIN_TEXT)); } } break; |