diff options
Diffstat (limited to 'servers/visual/shader_language.cpp')
-rw-r--r-- | servers/visual/shader_language.cpp | 632 |
1 files changed, 484 insertions, 148 deletions
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index d405dade6f..c2bdc6d7e4 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -283,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" }, @@ -862,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 ""; @@ -880,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; @@ -893,7 +896,7 @@ 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)) { @@ -924,6 +927,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; } @@ -946,7 +952,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; } } @@ -982,6 +990,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; } @@ -1228,7 +1239,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: @@ -2096,11 +2111,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); @@ -2108,6 +2124,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(); @@ -2287,7 +2304,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) { @@ -2317,8 +2337,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; } } @@ -2326,6 +2351,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(); @@ -2571,6 +2608,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; } @@ -3122,7 +3161,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; } @@ -3135,135 +3174,187 @@ 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 - int carg = -1; + const StringName &name = identifier; - bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + 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); - // 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; - } + for (int i = 0; i < pstruct->members.size(); i++) { + Node *nexpr = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!nexpr) { + return NULL; + } + Node *node = pstruct->members[i]; - //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; - } + 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; } - //see if texture arguments must connect - function_index = i; - break; + 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; } - } - if (carg >= 0) { - completion_type = COMPLETION_CALL_ARGUMENTS; - completion_line = tk_line; - completion_block = p_block; - completion_function = funcname->name; - completion_argument = carg; - } + expr = func; - if (!ok) - return NULL; + } else { //a function - 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 + const StringName &name = identifier; - if (function_index >= 0) { - //connect texture arguments, so we can cache in the - //argument what type of filter and repeat to use + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_CALL; + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = name; + func->arguments.push_back(funcname); - FunctionNode *call_function = shader->functions[function_index].function; - if (call_function) { + int carg = -1; - //get current base function - FunctionNode *base_function = NULL; - { - BlockNode *b = p_block; + bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); - while (b) { + // 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; + } - if (b->parent_function) { - base_function = b->parent_function; + //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; - } else { - b = b->parent_block; } } + + //see if texture arguments must connect + function_index = i; + break; } + } - 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() && 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; + 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 (!_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) { + + //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; } - } 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>(); + } + } + + 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() && 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; } - base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i); - found = true; - break; } + ERR_CONTINUE(!found); } - ERR_CONTINUE(!found); } } } } + expr = func; } - expr = func; - } else { //an identifier @@ -3273,6 +3364,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons 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; @@ -3291,7 +3383,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; } @@ -3355,6 +3447,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; @@ -3366,6 +3459,7 @@ 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; } } @@ -3407,23 +3501,50 @@ 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; DataType member_type = TYPE_VOID; + StringName member_struct_name = ""; 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; + 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: @@ -3543,13 +3664,15 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (!ok) { - _set_error("Invalid member for " + get_datatype_name(dt) + " expression: ." + ident); + _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->datatype = member_type; + mn->base_struct_name = st; + mn->struct_name = member_struct_name; mn->name = ident; mn->owner = expr; expr = mn; @@ -3969,7 +4092,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; @@ -4124,6 +4251,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 '}'"); @@ -4132,31 +4261,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; @@ -4188,6 +4336,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(); @@ -4200,7 +4349,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; @@ -4255,16 +4409,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; @@ -4309,13 +4476,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 += "]'"; @@ -4324,7 +4495,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 += "]'"; @@ -4362,8 +4537,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; } @@ -4409,7 +4584,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; @@ -4428,8 +4608,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(); @@ -4441,7 +4621,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; @@ -5091,6 +5276,107 @@ 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; + st_node->members.push_back(member); + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + 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: { @@ -5380,6 +5666,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; @@ -5394,18 +5682,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) { @@ -5442,7 +5743,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; @@ -5457,7 +5759,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; @@ -5465,7 +5766,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; } @@ -5525,6 +5831,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)) { @@ -5556,27 +5863,43 @@ 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)) { - _set_error("Expected a valid datatype for argument"); - return ERR_PARSE_ERROR; - } + is_struct = false; - ptype = get_token_datatype(tk.type); + 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 (_validate_datatype(ptype) != OK) { + if (!is_struct && !is_token_datatype(tk.type)) { + _set_error("Expected a valid datatype for argument"); 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(); @@ -5608,6 +5931,7 @@ 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; @@ -5868,6 +6192,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()) { |