/*************************************************************************/ /* shader_language.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "shader_language.h" #include "print_string.h" #include "os/os.h" static bool _is_text_char(CharType c) { return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; } static bool _is_number(CharType c) { return (c>='0' && c<='9'); } static bool _is_hex(CharType c) { return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); } const char * ShaderLanguage::token_names[TK_MAX]={ "EMPTY", "IDENTIFIER", "TRUE", "FALSE", "REAL_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_FLOAT", "TYPE_VEC2", "TYPE_VEC3", "TYPE_VEC4", "TYPE_MAT2", "TYPE_MAT3", "TYPE_MAT4", "TYPE_TEXTURE", "TYPE_CUBEMAP", "TYPE_COLOR", "OP_EQUAL", "OP_NOT_EQUAL", "OP_LESS", "OP_LESS_EQUAL", "OP_GREATER", "OP_GREATER_EQUAL", "OP_AND", "OP_OR", "OP_NOT", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV", "OP_NEG", "OP_ASSIGN", "OP_ASSIGN_ADD", "OP_ASSIGN_SUB", "OP_ASSIGN_MUL", "OP_ASSIGN_DIV", "CF_IF", "CF_ELSE", "CF_RETURN", "BRACKET_OPEN", "BRACKET_CLOSE", "CURLY_BRACKET_OPEN", "CURLY_BRACKET_CLOSE", "PARENTHESIS_OPEN", "PARENTHESIS_CLOSE", "COMMA", "SEMICOLON", "PERIOD", "UNIFORM", "ERROR", }; String ShaderLanguage::get_token_text(Token p_token) { String name=token_names[p_token.type]; if (p_token.type==TK_INT_CONSTANT || p_token.type==TK_REAL_CONSTANT) { name+="("+rtos(p_token.constant)+")"; } else if (p_token.type==TK_IDENTIFIER) { name+="("+String(p_token.text)+")"; } return name; } ShaderLanguage::Token ShaderLanguage::_make_token(TokenType p_type,const StringName& p_text) { Token tk; tk.type=p_type; tk.text=p_text; tk.line=tk_line; return tk; } ShaderLanguage::Token ShaderLanguage::_get_token() { #define GETCHAR(m_idx) ((char_idx': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_GREATER_EQUAL); } else if (GETCHAR(0)=='<') { char_idx++;; if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_SHIFT_RIGHT); } return _make_token(TK_OP_SHIFT_RIGHT); } return _make_token(TK_OP_GREATER); } break; case '!': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_NOT_EQUAL); } return _make_token(TK_OP_NOT); } break; //case '"' //string - no strings in shader //case '\'' //string - no strings in shader case '{': return _make_token(TK_CURLY_BRACKET_OPEN); case '}': return _make_token(TK_CURLY_BRACKET_CLOSE); case '[': return _make_token(TK_BRACKET_OPEN); case ']': return _make_token(TK_BRACKET_CLOSE); case '(': return _make_token(TK_PARENTHESIS_OPEN); case ')': return _make_token(TK_PARENTHESIS_CLOSE); case ',': return _make_token(TK_COMMA); case ';': return _make_token(TK_SEMICOLON); case '?': return _make_token(TK_QUESTION); case ':': return _make_token(TK_COLON); case '^': return _make_token(TK_OP_BIT_XOR); case '~': return _make_token(TK_OP_BIT_INVERT); case '&': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_BIT_AND); } else if (GETCHAR(0)=='&') { char_idx++; return _make_token(TK_OP_AND); } return _make_token(TK_OP_BIT_AND); } break; case '|': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_BIT_OR); } else if (GETCHAR(0)=='|') { char_idx++; return _make_token(TK_OP_OR); } return _make_token(TK_OP_BIT_OR); } break; case '*': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_MUL); } return _make_token(TK_OP_MUL); } break; case '+': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_ADD); } else if (GETCHAR(0)=='+') { char_idx++; return _make_token(TK_OP_INCREMENT); } return _make_token(TK_OP_ADD); } break; case '-': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_SUB); }else if (GETCHAR(0)=='-') { char_idx++; return _make_token(TK_OP_DECREMENT); } return _make_token(TK_OP_SUB); } break; case '%': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_MOD); } return _make_token(TK_OP_MOD); } break; default: { char_idx--; //go back one, since we have no idea what this is if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) { // parse number bool period_found=false; bool exponent_found=false; bool hexa_found=false; bool sign_found=false; bool minus_exponent_found=false; String str; int i=0; while(true) { if (GETCHAR(i)=='.') { if (period_found || exponent_found) return _make_token(TK_ERROR,"Invalid numeric constant"); period_found=true; } else if (GETCHAR(i)=='x') { if (hexa_found || str.length()!=1 || str[0]!='0') return _make_token(TK_ERROR,"Invalid numeric constant"); hexa_found=true; } else if (GETCHAR(i)=='e') { if (hexa_found || exponent_found) return _make_token(TK_ERROR,"Invalid numeric constant"); exponent_found=true; } else if (_is_number(GETCHAR(i))) { //all ok } else if (hexa_found && _is_hex(GETCHAR(i))) { } else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) { if (sign_found) return _make_token(TK_ERROR,"Invalid numeric constant"); sign_found=true; if (GETCHAR(i)=='-') minus_exponent_found=true; } else break; str+=CharType(GETCHAR(i)); i++; } if (!_is_number(str[str.length()-1])) return _make_token(TK_ERROR,"Invalid numeric constant"); char_idx+=str.length()-1; Token tk; if (period_found || minus_exponent_found) tk.type=TK_REAL_CONSTANT; else tk.type=TK_INT_CONSTANT; if (!str.is_valid_float()) { return _make_token(TK_ERROR,"Invalid numeric constant"); } tk.constant=str.to_double(); tk.line=tk_line; return tk; } if (GETCHAR(0)=='.') { //parse period return _make_token(TK_PERIOD); } if (_is_text_char(GETCHAR(0))) { // parse identifier String str; str+=CharType(GETCHAR(0)); while(_is_text_char(GETCHAR(1))) { str+=CharType(GETCHAR(1)); char_idx++; } //see if keyword //should be converted to a static map struct _kws { TokenType token; const char *text;}; static const _kws keyword_list[]={ {TK_TRUE,"true"}, {TK_FALSE,"false"}, {TK_TYPE_VOID,"void"}, {TK_TYPE_BOOL,"bool"}, {TK_TYPE_BVEC2,"bvec2"}, {TK_TYPE_BVEC3,"bvec3"}, {TK_TYPE_BVEC4,"bvec4"}, {TK_TYPE_INT,"int"}, {TK_TYPE_IVEC2,"ivec2"}, {TK_TYPE_IVEC3,"ivec3"}, {TK_TYPE_IVEC4,"ivec4"}, {TK_TYPE_UINT,"uint"}, {TK_TYPE_UVEC2,"uvec2"}, {TK_TYPE_UVEC3,"uvec3"}, {TK_TYPE_UVEC4,"uvec4"}, {TK_TYPE_FLOAT,"float"}, {TK_TYPE_VEC2,"vec2"}, {TK_TYPE_VEC3,"vec3"}, {TK_TYPE_VEC4,"vec4"}, {TK_TYPE_MAT2,"mat2"}, {TK_TYPE_MAT3,"mat3"}, {TK_TYPE_MAT4,"mat4"}, {TK_TYPE_SAMPLER2D,"sampler2D"}, {TK_TYPE_ISAMPLER2D,"isampler2D"}, {TK_TYPE_USAMPLER2D,"usampler2D"}, {TK_TYPE_SAMPLERCUBE,"samplerCube"}, {TK_PRECISION_LOW,"lowp"}, {TK_PRECISION_MID,"mediump"}, {TK_PRECISION_HIGH,"highp"}, {TK_CF_IF,"if"}, {TK_CF_ELSE,"else"}, {TK_CF_FOR,"for"}, {TK_CF_WHILE,"while"}, {TK_CF_DO,"do"}, {TK_CF_SWITCH,"switch"}, {TK_CF_CASE,"case"}, {TK_CF_BREAK,"break"}, {TK_CF_CONTINUE,"continue"}, {TK_CF_RETURN,"return"}, {TK_UNIFORM,"uniform"}, {TK_VARYING,"varying"}, {TK_RENDER_MODE,"render_mode"}, {TK_HINT_WHITE_TEXTURE,"hint_white"}, {TK_HINT_BLACK_TEXTURE,"hint_black"}, {TK_HINT_NORMAL_TEXTURE,"hint_normal"}, {TK_HINT_RANGE,"hint_range"}, {TK_ERROR,NULL} }; int idx=0; while(keyword_list[idx].text) { if (str==keyword_list[idx].text) { _make_token(keyword_list[idx].token); } idx++; } return _make_token(TK_IDENTIFIER,str); } if (GETCHAR(0)>32) return _make_token(TK_ERROR,"Tokenizer: Unknown character #"+itos(GETCHAR(0))+": '"+String::chr(GETCHAR(0))+"'"); else return _make_token(TK_ERROR,"Tokenizer: Unknown character #"+itos(GETCHAR(0))); } break; } } ERR_PRINT("BUG"); return Token(); } String ShaderLanguage::lex_debug(const String& p_code) { #if 0 Vector tokens; String error; int errline,errcol; if (tokenize(p_code,&tokens,&error,&errline,&errcol)!=OK) return error; String ret; for(int i=0;inext; memdelete(n); } } bool ShaderLanguage::_find_identifier(const BlockNode* p_block,const Map &p_builtin_types,const StringName& p_identifier, DataType *r_data_type, IdentifierType *r_type) { if (p_builtin_types.has(p_identifier)) { if (r_data_type) { *r_data_type=p_builtin_types[p_identifier]; } if (r_type) { *r_type=IDENTIFIER_BUILTIN_VAR; } return true; } FunctionNode *function=NULL; while(p_block) { if (p_block->variables.has(p_identifier)) { if (r_data_type) { *r_data_type=p_block->variables[p_identifier].type; } if (r_type) { *r_type=IDENTIFIER_LOCAL_VAR; } return true; } if (p_block->parent_function) { function=p_block->parent_function; break; } else { ERR_FAIL_COND_V(!p_block->parent_block,false); p_block=p_block->parent_block; } } if (function) { for(int i=0;iarguments.size();i++) { if (function->arguments[i].name==p_identifier) { if (r_data_type) { *r_data_type=function->arguments[i].type; } if (r_type) { *r_type=IDENTIFIER_FUNCTION_ARGUMENT; } return true; } } } if (shader->varyings.has(p_identifier)) { if (r_data_type) { *r_data_type=shader->varyings[p_identifier].type; } if (r_type) { *r_type=IDENTIFIER_VARYING; } return true; } if (shader->uniforms.has(p_identifier)) { if (r_data_type) { *r_data_type=shader->uniforms[p_identifier].type; } if (r_type) { *r_type=IDENTIFIER_UNIFORM; } return true; } for(int i=0;ifunctions.size();i++) { if (!shader->functions[i].callable) continue; if (shader->functions[i].name==p_identifier) { if (r_data_type) { *r_data_type=shader->functions[i].function->return_type; } if (r_type) { *r_type=IDENTIFIER_FUNCTION; } } } return false; } bool ShaderLanguage::_validate_operator(OperatorNode *p_op,DataType *r_ret_type) { bool valid=false; DataType ret_type; switch(p_op->op) { case OP_EQUAL: case OP_NOT_EQUAL: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid=na==nb; ret_type=TYPE_BOOL; } break; case OP_LESS: case OP_LESS_EQUAL: case OP_GREATER: case OP_GREATER_EQUAL: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid = na==nb && (na==TYPE_UINT || na==TYPE_INT || na==TYPE_FLOAT); ret_type=TYPE_BOOL; } break; case OP_AND: case OP_OR: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid = na==nb && na==TYPE_BOOL; ret_type=TYPE_BOOL; } break; case OP_NOT: { DataType na = p_op->arguments[0]->get_datatype(); valid = na==TYPE_BOOL; ret_type=TYPE_BOOL; } break; case OP_INCREMENT: case OP_DECREMENT: case OP_POST_INCREMENT: case OP_POST_DECREMENT: case OP_NEGATE: { DataType na = p_op->arguments[0]->get_datatype(); valid = na>TYPE_BOOL && naarguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na>nb) { //make things easier; SWAP(na,nb); } if (na==nb) { valid = (na>TYPE_BOOL && naop==OP_MUL && na>=TYPE_MAT2 && na<=TYPE_MAT4); ret_type=na; } else if (na==TYPE_INT && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_INT && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_INT && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_UINT && nb==TYPE_UVEC2) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UINT && nb==TYPE_UVEC3) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UINT && nb==TYPE_UVEC4) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_FLOAT && nb==TYPE_VEC2) { valid=true; ret_type=TYPE_VEC2; } else if (na==TYPE_FLOAT && nb==TYPE_VEC3) { valid=true; ret_type=TYPE_VEC3; } else if (na==TYPE_FLOAT && nb==TYPE_VEC4) { valid=true; ret_type=TYPE_VEC4; } else if (p_op->op==OP_MUL && na==TYPE_VEC2 && nb==TYPE_MAT2) { valid=true; ret_type=TYPE_MAT2; } else if (p_op->op==OP_MUL && na==TYPE_VEC3 && nb==TYPE_MAT3) { valid=true; ret_type=TYPE_MAT3; } else if (p_op->op==OP_MUL && na==TYPE_VEC4 && nb==TYPE_MAT4) { valid=true; ret_type=TYPE_MAT4; } } break; case OP_ASSIGN_MOD: case OP_MOD: { /* * The operator modulus (%) operates on signed or unsigned integers or integer vectors. The operand * types must both be signed or both be unsigned. The operands cannot be vectors of differing size. If * one operand is a scalar and the other vector, then the scalar is applied component-wise to the vector, * resulting in the same type as the vector. If both are vectors of the same size, the result is computed * component-wise. */ DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na==TYPE_INT && nb==TYPE_INT) { valid=true; ret_type=TYPE_INT; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_IVEC2 && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; ///// } else if (na==TYPE_UINT && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UINT; } else if (na==TYPE_UVEC2 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_UVEC2 && nb==TYPE_UVEC2) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UVEC3) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UVEC4) { valid=true; ret_type=TYPE_UVEC4; } } break; case OP_ASSIGN_SHIFT_LEFT: case OP_ASSIGN_SHIFT_RIGHT: case OP_SHIFT_LEFT: case OP_SHIFT_RIGHT: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na>=TYPE_UINT && na<=TYPE_UVEC4) { na=DataType(na-4); } if (nb>=TYPE_UINT && nb<=TYPE_UVEC4) { nb=DataType(nb-4); } if (na==TYPE_INT && nb==TYPE_INT) { valid=true; ret_type=TYPE_INT; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_IVEC2 && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; } } break; case OP_ASSIGN: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid=na==nb; ret_type=na; } break; case OP_ASSIGN_ADD: case OP_ASSIGN_SUB: case OP_ASSIGN_MUL: case OP_ASSIGN_DIV: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na==nb) { valid = (na>TYPE_BOOL && naop==OP_ASSIGN_MUL && na>=TYPE_MAT2 && na<=TYPE_MAT4); ret_type=na; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT ) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_UVEC2 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_VEC2 && nb==TYPE_FLOAT ) { valid=true; ret_type=TYPE_VEC2; } else if (na==TYPE_VEC3 && nb==TYPE_FLOAT) { valid=true; ret_type=TYPE_VEC3; } else if (na==TYPE_VEC4 && nb==TYPE_FLOAT) { valid=true; ret_type=TYPE_VEC4; } else if (p_op->op==OP_ASSIGN_MUL && na==TYPE_MAT2 && nb==TYPE_VEC2) { valid=true; ret_type=TYPE_MAT2; } else if (p_op->op==OP_ASSIGN_MUL && na==TYPE_MAT3 && nb==TYPE_VEC3) { valid=true; ret_type=TYPE_MAT3; } else if (p_op->op==OP_ASSIGN_MUL && na==TYPE_MAT4 && nb==TYPE_VEC4) { valid=true; ret_type=TYPE_MAT4; } } break; case OP_ASSIGN_BIT_AND: case OP_ASSIGN_BIT_OR: case OP_ASSIGN_BIT_XOR: case OP_BIT_AND: case OP_BIT_OR: case OP_BIT_XOR: { /* * The bitwise operators and (&), exclusive-or (^), and inclusive-or (|). The operands must be of type * signed or unsigned integers or integer vectors. The operands cannot be vectors of differing size. If * one operand is a scalar and the other a vector, the scalar is applied component-wise to the vector, * resulting in the same type as the vector. The fundamental types of the operands (signed or unsigned) * must match. */ DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na>nb && p_op->op>=OP_BIT_AND) { //can swap for non assign SWAP(na,nb); } if (na==TYPE_INT && nb==TYPE_INT) { valid=true; ret_type=TYPE_INT; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_IVEC2 && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; ///// } else if (na==TYPE_UINT && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UINT; } else if (na==TYPE_UVEC2 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_UVEC2 && nb==TYPE_UVEC2) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UVEC3) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UVEC4) { valid=true; ret_type=TYPE_UVEC4; } } break; case OP_BIT_INVERT: { //unaries DataType na = p_op->arguments[0]->get_datatype(); valid = na>=TYPE_INT && naarguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); DataType nc = p_op->arguments[2]->get_datatype(); valid = na==TYPE_BOOL && (nb==nc); ret_type=nb; } break; default: { ERR_FAIL_V(false); } } if (r_ret_type) *r_ret_type=ret_type; return valid; } const ShaderLanguage::IntrinsicFuncDef ShaderLanguage::intrinsic_func_defs[]={ //constructors {"bool",TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_BOOL,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BOOL,TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BVEC2,TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BOOL,TYPE_BVEC2,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BOOL,TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BVEC2,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC2,TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BOOL,TYPE_BVEC2,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BVEC3,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC3,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC2,TYPE_BVEC2,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_FLOAT,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC2,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_INT,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_INT,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_INT,TYPE_IVEC2,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_INT,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC2,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_INT,TYPE_IVEC2,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_IVEC3,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC3,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_UINT,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_UINT,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UVEC2,TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UINT,TYPE_UVEC2,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UVEC2,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC2,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UINT,TYPE_UVEC2,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UVEC3,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC3,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"mat2",TYPE_MAT2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"mat3",TYPE_MAT3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mat4",TYPE_MAT4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"mat2",TYPE_MAT2,{TYPE_FLOAT,TYPE_VOID}}, {"mat3",TYPE_MAT3,{TYPE_FLOAT,TYPE_VOID}}, {"mat4",TYPE_MAT4,{TYPE_FLOAT,TYPE_VOID}}, //conversion scalars {"int",TYPE_INT,{TYPE_BOOL,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_UINT,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_FLOAT,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_BOOL,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_INT,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_UINT,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_BOOL,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_INT,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_UINT,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_FLOAT,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_INT,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_UINT,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_FLOAT,TYPE_VOID}}, //conversion vectors {"ivec2",TYPE_IVEC2,{TYPE_BVEC2,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_VEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_BVEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_IVEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_UVEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_BVEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_VEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_BVEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_VEC2,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_BVEC3,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_VEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_BVEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_IVEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_UVEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_BVEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_VEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BVEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_VEC3,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_BVEC4,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_VEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_BVEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_IVEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_UVEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_BVEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_VEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_VEC4,TYPE_VOID}}, //intrinsics - trigonometry {"sin",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"cos",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"tan",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"asin",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"acos",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"atan",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"atan2",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"sinh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"cosh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"tanh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, //intrinsics - exponential {"pow",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"pow",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"pow",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"exp",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"exp",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"exp",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"exp",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"log",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"log",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"log",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"log",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"sqrt",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"sqrt",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"sqrt",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"sqrt",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, //intrinsics - common {"abs",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"abs",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"abs",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"abs",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"abs",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"abs",TYPE_IVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"abs",TYPE_IVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"abs",TYPE_IVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"abs",TYPE_UINT,{TYPE_UINT,TYPE_VOID}}, {"abs",TYPE_UVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"abs",TYPE_UVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"abs",TYPE_UVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"sign",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"sign",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"sign",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"sign",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"sign",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"sign",TYPE_IVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"sign",TYPE_IVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"sign",TYPE_IVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"floor",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"floor",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"floor",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"floor",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"trunc",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"trunc",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"trunc",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"trunc",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"round",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"round",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"round",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"round",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"ceil",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"ceil",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"ceil",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"ceil",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"fract",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"fract",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"fract",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"fract",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"mod",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"mod",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"mod",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"mod",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mod",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"mod",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"mod",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"modf",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"modf",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"modf",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"modf",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"min",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"min",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"min",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"min",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"min",TYPE_INT,{TYPE_INT,TYPE_INT,TYPE_VOID}}, {"min",TYPE_IVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"min",TYPE_IVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"min",TYPE_IVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"min",TYPE_UINT,{TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"min",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"min",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"min",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"max",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"max",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"max",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"max",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"max",TYPE_INT,{TYPE_INT,TYPE_INT,TYPE_VOID}}, {"max",TYPE_IVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"max",TYPE_IVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"max",TYPE_IVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"max",TYPE_UINT,{TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"max",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"max",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"max",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"clamp",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"clamp",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"clamp",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"clamp",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_INT,{TYPE_INT,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_IVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"clamp",TYPE_IVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"clamp",TYPE_IVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"clamp",TYPE_IVEC2,{TYPE_IVEC2,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_IVEC3,{TYPE_IVEC3,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_IVEC4,{TYPE_IVEC4,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_UINT,{TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"clamp",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"clamp",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"clamp",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"clamp",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"clamp",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"clamp",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"mix",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"step",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"step",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"step",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"step",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"step",TYPE_VEC2,{TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"step",TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}}, {"step",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC4,TYPE_VOID}}, {"smoothstep",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"smoothstep",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"smoothstep",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"smoothstep",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"smoothstep",TYPE_VEC2,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"smoothstep",TYPE_VEC3,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}}, {"smoothstep",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC4,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_FLOAT,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_VEC2,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_VEC3,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_VEC4,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_FLOAT,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_VEC2,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_VEC3,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_VEC4,TYPE_VOID}}, {"floatBitsToInt",TYPE_INT,{TYPE_FLOAT,TYPE_VOID}}, {"floatBitsToInt",TYPE_IVEC2,{TYPE_VEC2,TYPE_VOID}}, {"floatBitsToInt",TYPE_IVEC3,{TYPE_VEC3,TYPE_VOID}}, {"floatBitsToInt",TYPE_IVEC4,{TYPE_VEC4,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UINT,{TYPE_FLOAT,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UVEC2,{TYPE_VEC2,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UVEC3,{TYPE_VEC3,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UVEC4,{TYPE_VEC4,TYPE_VOID}}, {"intBitsToFloat",TYPE_FLOAT,{TYPE_INT,TYPE_VOID}}, {"intBitsToFloat",TYPE_VEC2,{TYPE_IVEC2,TYPE_VOID}}, {"intBitsToFloat",TYPE_VEC3,{TYPE_IVEC3,TYPE_VOID}}, {"intBitsToFloat",TYPE_VEC4,{TYPE_IVEC4,TYPE_VOID}}, {"uintBitsToFloat",TYPE_FLOAT,{TYPE_UINT,TYPE_VOID}}, {"uintBitsToFloat",TYPE_VEC2,{TYPE_UVEC2,TYPE_VOID}}, {"uintBitsToFloat",TYPE_VEC3,{TYPE_UVEC3,TYPE_VOID}}, {"uintBitsToFloat",TYPE_VEC4,{TYPE_UVEC4,TYPE_VOID}}, //intrinsics - geometric {"length",TYPE_FLOAT,{TYPE_VEC2,TYPE_VOID}}, {"length",TYPE_FLOAT,{TYPE_VEC3,TYPE_VOID}}, {"length",TYPE_FLOAT,{TYPE_VEC4,TYPE_VOID}}, {"distance",TYPE_FLOAT,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"distance",TYPE_FLOAT,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"distance",TYPE_FLOAT,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"dot",TYPE_FLOAT,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"dot",TYPE_FLOAT,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"dot",TYPE_FLOAT,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"cross",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"normalize",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"normalize",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"normalize",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"reflect",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"refract",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"facefordward",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"facefordward",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"facefordward",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"matrixCompMult",TYPE_MAT2,{TYPE_MAT2,TYPE_MAT2,TYPE_VOID}}, {"matrixCompMult",TYPE_MAT3,{TYPE_MAT3,TYPE_MAT3,TYPE_VOID}}, {"matrixCompMult",TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4,TYPE_VOID}}, {"outerProduct",TYPE_MAT2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"outerProduct",TYPE_MAT3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"outerProduct",TYPE_MAT4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"transpose",TYPE_MAT2,{TYPE_MAT2,TYPE_VOID}}, {"transpose",TYPE_MAT3,{TYPE_MAT3,TYPE_VOID}}, {"transpose",TYPE_MAT4,{TYPE_MAT4,TYPE_VOID}}, {"determinant",TYPE_FLOAT,{TYPE_MAT2,TYPE_VOID}}, {"determinant",TYPE_FLOAT,{TYPE_MAT3,TYPE_VOID}}, {"determinant",TYPE_FLOAT,{TYPE_MAT4,TYPE_VOID}}, {"inverse",TYPE_MAT2,{TYPE_MAT2,TYPE_VOID}}, {"inverse",TYPE_MAT3,{TYPE_MAT3,TYPE_VOID}}, {"inverse",TYPE_MAT4,{TYPE_MAT4,TYPE_VOID}}, {"lessThan",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"lessThan",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"lessThan",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"lessThan",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"lessThan",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"lessThan",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"lessThan",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"lessThan",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"lessThan",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"greaterThan",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"greaterThan",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"greaterThan",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"greaterThan",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"greaterThan",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"greaterThan",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"greaterThan",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"greaterThan",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"greaterThan",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_BVEC2,TYPE_BVEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_BVEC3,TYPE_BVEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_BVEC4,TYPE_BVEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_BVEC2,TYPE_BVEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_BVEC3,TYPE_BVEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_BVEC4,TYPE_BVEC4,TYPE_VOID}}, {"any",TYPE_BOOL,{TYPE_BVEC2,TYPE_VOID}}, {"any",TYPE_BOOL,{TYPE_BVEC3,TYPE_VOID}}, {"any",TYPE_BOOL,{TYPE_BVEC4,TYPE_VOID}}, {"all",TYPE_BOOL,{TYPE_BVEC2,TYPE_VOID}}, {"all",TYPE_BOOL,{TYPE_BVEC3,TYPE_VOID}}, {"all",TYPE_BOOL,{TYPE_BVEC4,TYPE_VOID}}, {"not",TYPE_BOOL,{TYPE_BVEC2,TYPE_VOID}}, {"not",TYPE_BOOL,{TYPE_BVEC3,TYPE_VOID}}, {"not",TYPE_BOOL,{TYPE_BVEC4,TYPE_VOID}}, //intrinsics - texture {"textureSize",TYPE_VEC2,{TYPE_SAMPLER2D,TYPE_INT,TYPE_VOID}}, {"textureSize",TYPE_VEC2,{TYPE_ISAMPLER2D,TYPE_INT,TYPE_VOID}}, {"textureSize",TYPE_VEC2,{TYPE_USAMPLER2D,TYPE_INT,TYPE_VOID}}, {"textureSize",TYPE_VEC2,{TYPE_SAMPLERCUBE,TYPE_INT,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC2,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC2,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC2,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC4,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC4,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC4,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texelFetch",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"texelFetch",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"texelFetch",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"textureProjLod",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureGrad",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"textureGrad",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"textureGrad",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"textureGrad",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"textureScreen",TYPE_VEC4,{TYPE_VEC2,TYPE_VOID}}, {"dFdx",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"dFdx",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"dFdx",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"dFdx",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"dFdy",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"dFdy",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"dFdy",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"dFdy",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"fwidth",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"fwidth",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"fwidth",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"fwidth",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {NULL,TYPE_VOID,{TYPE_VOID}} }; bool ShaderLanguage::_validate_function_call(BlockNode* p_block, OperatorNode *p_func,DataType *r_ret_type) { ERR_FAIL_COND_V(p_func->op!=OP_CALL && p_func->op!=OP_CONSTRUCT,NULL); Vector args; ERR_FAIL_COND_V( p_func->arguments[0]->type!=Node::TYPE_VARIABLE, NULL ); StringName name = static_cast(p_func->arguments[0])->name.operator String(); bool all_const=true; for(int i=1;iarguments.size();i++) { if (p_func->arguments[i]->type!=Node::TYPE_CONSTANT) all_const=false; args.push_back(p_func->arguments[i]->get_datatype()); } int argcount=args.size(); bool found_intrinsic=false; if (argcount<=4) { // test intrinsics int idx=0; while (intrinsic_func_defs[idx].name) { if (name==intrinsic_func_defs[idx].name) { bool fail=false; for(int i=0;iop==OP_CONSTRUCT && all_const) { Vector cdata; for(int i=0;i(p_func->arguments[i+1])->value; switch(v.get_type()) { case Variant::REAL: cdata.push_back(v); break; case Variant::INT: cdata.push_back(v); break; case Variant::VECTOR2: { Vector2 v2=v; cdata.push_back(v2.x); cdata.push_back(v2.y); } break; case Variant::VECTOR3: { Vector3 v3=v; cdata.push_back(v3.x); cdata.push_back(v3.y); cdata.push_back(v3.z);} break; case Variant::PLANE: { Plane v4=v; cdata.push_back(v4.normal.x); cdata.push_back(v4.normal.y); cdata.push_back(v4.normal.z); cdata.push_back(v4.d); } break; default: ERR_FAIL_V(NULL); } } ConstantNode *cn = parser.create_node(p_func->parent); Variant data; switch(p_func->return_cache) { case TYPE_FLOAT: data = cdata[0]; break; case TYPE_VEC2: if (cdata.size()==1) data = Vector2(cdata[0],cdata[0]); else data = Vector2(cdata[0],cdata[1]); break; case TYPE_VEC3: if (cdata.size()==1) data = Vector3(cdata[0],cdata[0],cdata[0]); else data = Vector3(cdata[0],cdata[1],cdata[2]); break; case TYPE_VEC4: if (cdata.size()==1) data = Plane(cdata[0],cdata[0],cdata[0],cdata[0]); else data = Plane(cdata[0],cdata[1],cdata[2],cdata[3]); break; } cn->datatype=p_func->return_cache; cn->value=data; return cn; } return p_func; } #endif // try existing functions.. StringName exclude_function; BlockNode *block = p_block; while(block) { if (block->parent_function) { exclude_function=block->parent_function->name; } block=block->parent_block; } for(int i=0;ifunctions.size();i++) { if (shader->functions[i].name==exclude_function) { _set_error("Recursion is not allowed"); return false; } if (!shader->functions[i].callable) { _set_error("Function '"+String(name)+" can't be called from source code."); return false; } if (name != shader->functions[i].name) continue; FunctionNode *pfunc = shader->functions[i].function; if (pfunc->arguments.size()!=args.size()) continue; bool fail=false; for(int i=0;iarguments[i].type) { fail=true; break; } } if (!fail) { p_func->return_cache=pfunc->return_type; return true; } } return false; } bool ShaderLanguage::_parse_function_arguments(BlockNode* p_block,const Map &p_builtin_types,OperatorNode* p_func) { Token tk = _get_token(); while(true) { if (tk.type==TK_PARENTHESIS_CLOSE) { return true; } Node *arg= _parse_expression(p_block,p_builtin_types); if (!arg) return false; p_func->arguments.push_back(arg); tk = _get_token(); if (tk.type==TK_PARENTHESIS_CLOSE) { //none } else if (tk.type==TK_COMMA) { tk = _get_token(); //next } else { // something is broken _set_error("Expected ',' or ')' after argument"); return false; } } return true; } bool ShaderLanguage::is_token_operator(TokenType p_type) { return (p_type==TK_OP_EQUAL || p_type==TK_OP_NOT_EQUAL || p_type==TK_OP_LESS || p_type==TK_OP_LESS_EQUAL || p_type==TK_OP_GREATER || p_type==TK_OP_GREATER_EQUAL || p_type==TK_OP_AND || p_type==TK_OP_OR || p_type==TK_OP_NOT || p_type==TK_OP_ADD || p_type==TK_OP_SUB || p_type==TK_OP_MUL || p_type==TK_OP_DIV || p_type==TK_OP_MOD || p_type==TK_OP_SHIFT_LEFT || p_type==TK_OP_SHIFT_RIGHT || p_type==TK_OP_ASSIGN || p_type==TK_OP_ASSIGN_ADD || p_type==TK_OP_ASSIGN_SUB || p_type==TK_OP_ASSIGN_MUL || p_type==TK_OP_ASSIGN_DIV || p_type==TK_OP_ASSIGN_MOD || p_type==TK_OP_ASSIGN_SHIFT_LEFT || p_type==TK_OP_ASSIGN_SHIFT_RIGHT || p_type==TK_OP_ASSIGN_BIT_AND || p_type==TK_OP_ASSIGN_BIT_OR || p_type==TK_OP_ASSIGN_BIT_XOR || p_type==TK_OP_BIT_AND || p_type==TK_OP_BIT_OR || p_type==TK_OP_BIT_XOR || p_type==TK_OP_BIT_INVERT || p_type==TK_OP_INCREMENT || p_type==TK_OP_DECREMENT || p_type==TK_QUESTION || p_type==TK_COLON ); } ShaderLanguage::Node* ShaderLanguage::_parse_expression(BlockNode* p_block,const Map &p_builtin_types) { Vector expression; //Vector operators; while(true) { Node *expr=NULL; int pos = char_idx; Token tk = _get_token(); if (tk.type==TK_PARENTHESIS_OPEN) { //handle subexpression expr = _parse_expression(p_block,p_builtin_types); if (!expr) return NULL; tk = _get_token(); if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected ')' in expression"); return NULL; } } else if (tk.type==TK_REAL_CONSTANT) { ConstantNode *constant = alloc_node(); constant->value=tk.constant; constant->datatype=TYPE_FLOAT; expr=constant; } else if (tk.type==TK_REAL_CONSTANT) { ConstantNode *constant = alloc_node(); constant->value=int(tk.constant); constant->datatype=TYPE_INT; expr=constant; } else if (tk.type==TK_TRUE) { //print_line("found true"); //handle true constant ConstantNode *constant = alloc_node(); constant->value=true; constant->datatype=TYPE_BOOL; expr=constant; } else if (tk.type==TK_FALSE) { //handle false constant ConstantNode *constant = alloc_node(); constant->value=false; constant->datatype=TYPE_BOOL; expr=constant; } else if (tk.type==TK_TYPE_VOID) { //make sure void is not used in expression _set_error("Void value not allowed in Expression"); return NULL; } else if (is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type)) { //basic type constructor OperatorNode *func = alloc_node(); func->op=OP_CONSTRUCT; if (is_token_precision(tk.type)) { func->return_precision_cache=get_token_precision(tk.type); tk=_get_token(); } VariableNode *funcname = alloc_node(); funcname->name=get_datatype_name(get_token_datatype(tk.type)); func->arguments.push_back(funcname); tk=_get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after type name"); return NULL; } if (!_parse_function_arguments(p_block,p_builtin_types,func)) return NULL; if (!_validate_function_call(p_block,func,&func->return_cache)) { _set_error("No matching constructor found for: '"+String(funcname->name)+"'"); return NULL; } //validate_Function_call() expr=func; } else if (tk.type==TK_IDENTIFIER) { tk=_get_token(); if (tk.type==TK_PARENTHESIS_OPEN) { //a function StringName name = tk.text; OperatorNode *func = alloc_node(); func->op=OP_CALL; VariableNode *funcname = alloc_node(); funcname->name=name; func->arguments.push_back(funcname); if (!_parse_function_arguments(p_block,p_builtin_types,func)) return NULL; if (!_validate_function_call(p_block,func,&func->return_cache)) { _set_error("No matching function found for: '"+String(funcname->name)+"'"); return NULL; } expr=func; } else { //an identifier char_idx=pos; //rollback StringName identifier = tk.text; DataType data_type; IdentifierType ident_type; if (!_find_identifier(p_block,p_builtin_types,identifier,&data_type,&ident_type)) { _set_error("Unknown identifier in expression: "+String(identifier)); return NULL; } if (ident_type==IDENTIFIER_FUNCTION) { _set_error("Can't use function as identifier: "+String(identifier)); return NULL; } VariableNode *varname = alloc_node(); varname->name=identifier; varname->datatype_cache=data_type; expr=varname; } } else if (tk.type==TK_OP_ADD) { continue; //this one does nothing } 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) { Expression e; e.is_op=true; switch(tk.type) { case TK_OP_SUB: e.op=OP_NEGATE; break; case TK_OP_NOT: e.op=OP_NOT; break; case TK_OP_BIT_INVERT: e.op=OP_BIT_INVERT; break; case TK_OP_INCREMENT: e.op=OP_INCREMENT; break; case TK_OP_DECREMENT: e.op=OP_DECREMENT; break; default: ERR_FAIL_V(NULL); } expression.push_back(e); continue; } else { _set_error("Expected expression, found: "+get_token_text(tk)); return NULL; //nothing } ERR_FAIL_COND_V(!expr,NULL); /* OK now see what's NEXT to the operator.. */ /* OK now see what's NEXT to the operator.. */ /* OK now see what's NEXT to the operator.. */ while(true) { int pos = char_idx; tk=_get_token(); if (tk.type==TK_PERIOD) { tk=_get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier as member"); return NULL; } DataType dt = expr->get_datatype(); String ident = tk.text; bool ok=true; DataType member_type; switch(dt) { case TYPE_BVEC2: case TYPE_IVEC2: case TYPE_UVEC2: case TYPE_VEC2: { int l = ident.length(); if (l==1) { member_type=DataType(dt-1); } else if (l==2) { member_type=dt; } else { ok=false; break; } const CharType *c=ident.ptr(); for(int i=0;i(); mn->basetype=dt; mn->datatype=member_type; mn->name=ident; mn->owner=expr; expr=mn; //todo //member (period) has priority over any operator //creates a subindexing expression in place /*} else if (tk.type==TK_BRACKET_OPEN) { //todo //subindexing has priority over any operator //creates a subindexing expression in place */ } else if (tk.type==TK_OP_INCREMENT || tk.type==TK_OP_DECREMENT) { OperatorNode *op = alloc_node(); op->op=tk.type==TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); if (!_validate_operator(op,&op->return_cache)) { _set_error("Invalid base type for increment/decrement operator"); return NULL; } expr=op; } else { char_idx=pos; //rollback break; } } Expression e; e.is_op=false; e.node=expr; expression.push_back(e); pos = char_idx; tk = _get_token(); if (is_token_operator(tk.type)) { Expression o; o.is_op=true; switch(tk.type) { case TK_OP_EQUAL: o.op = OP_EQUAL; break; case TK_OP_NOT_EQUAL: o.op = OP_NOT_EQUAL; break; case TK_OP_LESS: o.op = OP_LESS; break; case TK_OP_LESS_EQUAL: o.op = OP_LESS_EQUAL; break; case TK_OP_GREATER: o.op = OP_GREATER; break; case TK_OP_GREATER_EQUAL: o.op = OP_GREATER_EQUAL; break; case TK_OP_AND: o.op = OP_AND; break; case TK_OP_OR: o.op = OP_OR; break; case TK_OP_ADD: o.op = OP_ADD; break; case TK_OP_SUB: o.op = OP_SUB; break; case TK_OP_MUL: o.op = OP_MUL; break; case TK_OP_DIV: o.op = OP_DIV; break; case TK_OP_MOD: o.op = OP_MOD; break; case TK_OP_SHIFT_LEFT: o.op = OP_SHIFT_LEFT; break; case TK_OP_SHIFT_RIGHT: o.op = OP_SHIFT_RIGHT; break; case TK_OP_ASSIGN: o.op = OP_ASSIGN; break; case TK_OP_ASSIGN_ADD: o.op = OP_ASSIGN_ADD; break; case TK_OP_ASSIGN_SUB: o.op = OP_ASSIGN_SUB; break; case TK_OP_ASSIGN_MUL: o.op = OP_ASSIGN_MUL; break; case TK_OP_ASSIGN_DIV: o.op = OP_ASSIGN_DIV; break; case TK_OP_ASSIGN_MOD: o.op = OP_ASSIGN_MOD; break; case TK_OP_ASSIGN_SHIFT_LEFT: o.op = OP_ASSIGN_SHIFT_LEFT; break; case TK_OP_ASSIGN_SHIFT_RIGHT: o.op = OP_ASSIGN_SHIFT_RIGHT; break; case TK_OP_ASSIGN_BIT_AND: o.op = OP_ASSIGN_BIT_AND; break; case TK_OP_ASSIGN_BIT_OR: o.op = OP_ASSIGN_BIT_OR; break; case TK_OP_ASSIGN_BIT_XOR: o.op = OP_ASSIGN_BIT_XOR; break; case TK_OP_BIT_AND: o.op = OP_BIT_AND; break; case TK_OP_BIT_OR: o.op = OP_BIT_OR ; break; case TK_OP_BIT_XOR: o.op = OP_BIT_XOR; break; case TK_QUESTION: o.op = OP_SELECT_IF; break; case TK_COLON: o.op = OP_SELECT_ELSE; break; default: { _set_error("Invalid token for operator: "+get_token_text(tk)); return NULL; } } break; expression.push_back(o); } else { char_idx=pos; //something else, so rollback and end break; } } /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ while(expression.size()>1) { int next_op=-1; int min_priority=0xFFFFF; bool is_unary=false; bool is_ternary=false; for(int i=0;i=next_op;i--) { OperatorNode *op = alloc_node(); op->op=expression[i].op; op->arguments.push_back(expression[i+1].node); expression[i].is_op=false; expression[i].node=op; if (!_validate_operator(op,&op->return_cache)) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(op->arguments[i]->get_datatype()); } _set_error("Invalid argument to unary operator: "+at); return NULL; } expression.remove(i+1); } } else if (is_ternary) { if (next_op <1 || next_op>=(expression.size()-1)) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } if (next_op+2 >= expression.size() || !expression[next_op+2].is_op || expression[next_op+2].op!=OP_SELECT_ELSE) { _set_error("Mising matching ':' for select operator"); return NULL; } OperatorNode *op = alloc_node(); op->op=expression[next_op].op; op->arguments.push_back(expression[next_op-1].node); op->arguments.push_back(expression[next_op+1].node); op->arguments.push_back(expression[next_op+3].node); expression[next_op-1].is_op=false; expression[next_op-1].node=op; if (!_validate_operator(op,&op->return_cache)) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(op->arguments[i]->get_datatype()); } _set_error("Invalid argument to ternary ?: operator: "+at); return NULL; } for(int i=0;i<4;i++) { expression.remove(next_op); } } else { if (next_op <1 || next_op>=(expression.size()-1)) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } OperatorNode *op = alloc_node(); op->op=expression[next_op].op; if (expression[next_op-1].is_op) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } if (expression[next_op+1].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op // can be followed by an unary op in a valid combination, // due to how precedence works, unaries will always dissapear first _set_error("Parser bug.."); } op->arguments.push_back(expression[next_op-1].node); //expression goes as left op->arguments.push_back(expression[next_op+1].node); //next expression goes as right expression[next_op-1].node=op; //replace all 3 nodes by this operator and make it an expression if (!_validate_operator(op,&op->return_cache)) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(op->arguments[i]->get_datatype()); } _set_error("Invalid arguments to operator: "+at); return NULL; } expression.remove(next_op); expression.remove(next_op); } } return expression[0].node; } ShaderLanguage::Node* ShaderLanguage::_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node) { if (p_node->type!=Node::TYPE_OPERATOR) return p_node; //for now only reduce simple constructors OperatorNode *op=static_cast(p_node); if (op->op==OP_CONSTRUCT) { ERR_FAIL_COND_V(op->arguments[0]->type!=Node::TYPE_VARIABLE,p_node); VariableNode *vn = static_cast(op->arguments[0]); StringName name=vn->name; if (name=="vec2" || name=="vec3" || name=="vec4") { Vector values; for(int i=1;iarguments.size();i++) { op->arguments[i]=_reduce_expression(p_block,op->arguments[i]); if (op->arguments[i]->type==Node::TYPE_CONSTANT) { ConstantNode *cn = static_cast(op->arguments[i]); values.push_back(cn->value); } else { return p_node; //do not bother, not reducible } } ConstantNode *cn=alloc_node(); if (name=="vec2") { cn->datatype=TYPE_VEC2; cn->value=Vector2(values[0],values[1]); } else if (name=="vec3") { cn->datatype=TYPE_VEC3; cn->value=Vector3(values[0],values[1],values[2]); } else if (name=="vec4") { cn->datatype=TYPE_VEC4; cn->value=Plane(values[0],values[1],values[2],values[3]); } else { ERR_FAIL_V(p_node); } return cn; } } return p_node; } ShaderLanguage::Node* ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const Map &p_builtin_types) { ShaderLanguage::Node* expr = _parse_expression(p_block,p_builtin_types); expr = _reduce_expression(p_block,expr); return expr; } Error ShaderLanguage::_parse_block(BlockNode* p_block,const Map &p_builtin_types,bool p_just_one,bool p_can_break,bool p_can_continue) { while(true) { int pos = char_idx; Token tk = _get_token(); if (tk.type==TK_CURLY_BRACKET_CLOSE) { //end of block if (p_just_one) { _set_error("Unexpected '}'"); return ERR_PARSE_ERROR; } return OK; } else if (is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type)) { DataPrecision precision=PRECISION_DEFAULT; if (is_token_precision(tk.type)) { precision=get_token_precision(tk.type); tk = _get_token(); if (!is_token_nonvoid_datatype(tk.type)) { _set_error("Expected datatype after precission"); return ERR_PARSE_ERROR; } } DataType type = get_token_datatype(tk.type); tk = _get_token(); while(true) { if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier after type"); return ERR_PARSE_ERROR; } StringName name = tk.text; if (_find_identifier(p_block,p_builtin_types,name)) { _set_error("Redefinition of '"+String(name)+"'"); return ERR_PARSE_ERROR; } BlockNode::Variable var; var.type=type; var.precision=precision; p_block->variables[name]=var; tk = _get_token(); if (tk.type==TK_OP_ASSIGN) { //variable creted with assignment! must parse an expression Node* n = _parse_and_reduce_expression(p_block,p_builtin_types); if (!n) return ERR_PARSE_ERROR; OperatorNode *assign = alloc_node(); VariableNode *vnode = alloc_node(); vnode->name=name; vnode->datatype_cache=type; assign->arguments.push_back(vnode); assign->arguments.push_back(n); assign->op=OP_ASSIGN; p_block->statements.push_back(assign); tk = _get_token(); } if (tk.type==TK_COMMA) { tk = _get_token(); //another variable } else if (tk.type==TK_SEMICOLON) { break; } else { _set_error("Expected ',' or ';' after variable"); return ERR_PARSE_ERROR; } } } else if (tk.type==TK_CURLY_BRACKET_OPEN) { //a sub block, just because.. BlockNode* block = alloc_node(); block->parent_block=p_block; _parse_block(block,p_builtin_types,false,p_can_break,p_can_continue); } else if (tk.type==TK_CF_IF) { //if () {} tk = _get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after if"); return ERR_PARSE_ERROR; } ControlFlowNode *cf = alloc_node(); cf->flow_op=FLOW_OP_IF; Node* n = _parse_and_reduce_expression(p_block,p_builtin_types); if (!n) return ERR_PARSE_ERROR; tk = _get_token(); if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected '(' after expression"); return ERR_PARSE_ERROR; } BlockNode* block = alloc_node(); block->parent_block=p_block; cf->blocks.push_back(block); Error err=_parse_block(p_block,p_builtin_types,true,p_can_break,p_can_continue); pos=char_idx; tk = _get_token(); if (tk.type==TK_CF_ELSE) { block = alloc_node(); block->parent_block=p_block; cf->blocks.push_back(block); err=_parse_block(p_block,p_builtin_types,true,p_can_break,p_can_continue); } else { char_idx=pos; //rollback } } else { //nothng else, so expression char_idx=pos; //rollback _parse_and_reduce_expression(p_block,p_builtin_types); tk = _get_token(); if (tk.type!=TK_SEMICOLON) { _set_error("Expected ';' after statement"); return ERR_PARSE_ERROR; } } if (p_just_one) break; } return OK; } Error ShaderLanguage::_parse_shader(const Map< StringName, Map > &p_functions, const Set &p_render_modes) { Token tk = _get_token(); while(tk.type!=TK_EOF) { switch(tk.type) { case TK_RENDER_MODE: { while(true) { tk = _get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier for render mode"); return ERR_PARSE_ERROR; } if (!p_render_modes.has(tk.text)) { _set_error("Invalid render mode: '"+String(tk.text)+"'"); return ERR_PARSE_ERROR; } if (shader->render_modes.has(tk.text)) { _set_error("Duplicate render mode: '"+String(tk.text)+"'"); return ERR_PARSE_ERROR; } shader->render_modes.insert(tk.text); tk = _get_token(); if (tk.type==TK_COMMA) { //all good, do nothing } else if (tk.type==TK_SEMICOLON) { break; //done } else { _set_error("Unexpected token: "+get_token_text(tk)); return ERR_PARSE_ERROR; } } } break; case TK_UNIFORM: case TK_VARYING: { bool uniform = tk.type==TK_UNIFORM; DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; tk = _get_token(); if (is_token_precision(tk.type)) { precision=get_token_precision(tk.type); tk = _get_token(); } if (!is_token_datatype(tk.type)) { _set_error("Expected datatype. "); return ERR_PARSE_ERROR; } type = get_token_datatype(tk.type); if (type==TYPE_VOID) { _set_error("void datatype not allowed here"); return ERR_PARSE_ERROR; } if (!uniform && typeTYPE_VEC4) { _set_error("Invalid type for varying, only float,vec2,vec3,vec4 allowed."); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier!"); return ERR_PARSE_ERROR; } name=tk.text; if (_find_identifier(NULL,Map(),name)) { _set_error("Redefinition of '"+String(name)+"'"); return ERR_PARSE_ERROR; } if (uniform) { ShaderNode::Uniform uniform; uniform.order=shader->uniforms.size(); uniform.type=type; uniform.precission=precision; shader->uniforms[name]=uniform; //todo parse default value tk = _get_token(); if (tk.type==TK_OP_ASSIGN) { Node* expr = _parse_and_reduce_expression(NULL,Map()); if (!expr) return ERR_PARSE_ERROR; if (expr->type!=Node::TYPE_CONSTANT) { _set_error("Expected constant expression after '='"); return ERR_PARSE_ERROR; } uniform.default_value=static_cast(expr)->value; tk = _get_token(); } if (tk.type==TK_COLON) { //hint tk = _get_token(); if (tk.type==TK_HINT_WHITE_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_WHITE_TEXTURE; } else if (tk.type==TK_HINT_BLACK_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_BLACK_TEXTURE; } else if (tk.type==TK_HINT_NORMAL_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_NORMAL_TEXTURE; } else if (tk.type==TK_HINT_RANGE) { uniform.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; } tk = _get_token(); if (tk.type!=TK_REAL_CONSTANT ||tk.type!=TK_INT_CONSTANT) { _set_error("Expected integer constant"); return ERR_PARSE_ERROR; } uniform.hint_range[0]=tk.constant; tk = _get_token(); if (tk.type!=TK_COMMA) { _set_error("Expected ',' after integer constant"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_REAL_CONSTANT || tk.type!=TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } uniform.hint_range[1]=tk.constant; tk = _get_token(); 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; } uniform.hint_range[2]=tk.constant; tk = _get_token(); } else { if (type==TYPE_INT) { uniform.hint_range[2]=1; } else { uniform.hint_range[2]=0.001; } } if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected ','"); return ERR_PARSE_ERROR; } } if (uniform.hint!=ShaderNode::Uniform::HINT_RANGE && uniform.hint!=ShaderNode::Uniform::HINT_NONE && type <=TYPE_MAT4) { _set_error("This hint is only for sampler types"); return ERR_PARSE_ERROR; } } if (tk.type!=TK_SEMICOLON) { _set_error("Expected ';'"); return ERR_PARSE_ERROR; } } else { ShaderNode::Varying varying; varying.type=type; varying.precission=precision; shader->varyings[name]=varying; tk = _get_token(); if (tk.type!=TK_SEMICOLON) { _set_error("Expected ';'"); return ERR_PARSE_ERROR; } } } break; default: { //function DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; if (is_token_precision(tk.type)) { precision=get_token_precision(tk.type); tk = _get_token(); } if (!is_token_datatype(tk.type)) { _set_error("Expected funtion, uniform or varying "); return ERR_PARSE_ERROR; } type = get_token_datatype(tk.type); tk = _get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected function name after datatype"); return ERR_PARSE_ERROR; } name=tk.text; if (_find_identifier(NULL,Map(),name)) { _set_error("Redefinition of '"+String(name)+"'"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after identifier"); return ERR_PARSE_ERROR; } Map builtin_types; if (p_functions.has(name)) { builtin_types=p_functions[name]; } ShaderNode::Function function; function.callable=!p_functions.has(name); function.name=name; FunctionNode* func_node=alloc_node(); function.function=func_node; shader->functions.push_back(function); func_node->name=name; func_node->return_type=type; func_node->return_precision=precision; func_node->body = alloc_node(); func_node->body->parent_function=func_node; tk = _get_token(); while(true) { if (tk.type==TK_PARENTHESIS_CLOSE) { break; } DataType ptype; StringName pname; DataPrecision pprecision = PRECISION_DEFAULT; if (is_token_precision(tk.type)) { pprecision=get_token_precision(tk.type); tk = _get_token(); } if (!is_token_datatype(tk.type)) { _set_error("Expected a valid datatype for argument"); return ERR_PARSE_ERROR; } ptype=get_token_datatype(tk.type); if (ptype==TYPE_VOID) { _set_error("void not allowed in argument"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier for argument name"); return ERR_PARSE_ERROR; } pname = tk.text; if (_find_identifier(func_node->body,builtin_types,pname)) { _set_error("Redefinition of '"+String(pname)+"'"); return ERR_PARSE_ERROR; } FunctionNode::Argument arg; arg.type=ptype; arg.name=pname; arg.precision=pprecision; func_node->arguments.push_back(arg); tk = _get_token(); if (tk.type==TK_COMMA) { tk = _get_token(); //do none and go on } else if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected ',' or ')' after identifier"); return ERR_PARSE_ERROR; } } if (p_functions.has(name)) { //if one of the core functions, make sure they are of the correct form if (func_node->arguments.size() > 0) { _set_error("Function '"+String(name)+"' expects no arguments."); return ERR_PARSE_ERROR; } if (func_node->return_type!=TYPE_VOID) { _set_error("Function '"+String(name)+"' must be of void return type."); return ERR_PARSE_ERROR; } } //all good let's parse inside the fucntion! tk = _get_token(); if (tk.type!=TK_CURLY_BRACKET_OPEN) { _set_error("Expected '{' to begin function"); return ERR_PARSE_ERROR; } Error err = _parse_block(func_node->body,builtin_types); if (err) return err; } } tk = _get_token(); } return OK; } Error ShaderLanguage::compile(const String& p_code, const Map< StringName, Map > &p_functions, const Set &p_render_modes) { clear(); code=p_code; nodes=NULL; shader = alloc_node(); Error err = _parse_shader(p_functions,p_render_modes); if (err!=OK) { return err; } return OK; } String ShaderLanguage::get_error_text() { return error_str; } int ShaderLanguage::get_error_line() { return error_line; } ShaderLanguage::ShaderLanguage() { nodes=NULL; } ShaderLanguage::~ShaderLanguage() { clear(); }