/*************************************************************************/ /* shader_language.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2014 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", "INDENTIFIER", "TRUE", "FALSE", "REAL_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_FLOAT", "TYPE_VEC2", "TYPE_VEC3", "TYPE_VEC4", "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", }; ShaderLanguage::Token ShaderLanguage::read_token(const CharType* p_text,int p_len,int &r_line,int &r_chars) { #define GETCHAR(m_idx) ((m_idx': { if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_GREATER_EQUAL); }/* else if (GETCHAR(1)=='<') { r_chars++; if (GETCHAR(2)=='=') { r_chars++; return Token(TK_OP_ASSIGN_SHIFT_RIGHT); } return Token(TK_OP_SHIFT_RIGHT); }*/ return Token(TK_OP_GREATER); } break; case '!': { if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_NOT_EQUAL); } return Token(TK_OP_NOT); } break; //case '"' //string - no strings in shader //case '\'' //string - no strings in shader case '{': return Token(TK_CURLY_BRACKET_OPEN); case '}': return Token(TK_CURLY_BRACKET_CLOSE); //case '[': // return Token(TK_BRACKET_OPEN); //case ']': // return Token(TK_BRACKET_CLOSE); case '(': return Token(TK_PARENTHESIS_OPEN); case ')': return Token(TK_PARENTHESIS_CLOSE); case ',': return Token(TK_COMMA); case ';': return Token(TK_SEMICOLON); //case '?': // return Token(TK_QUESTION_MARK); //case ':': // return Token(TK_COLON); //for methods maybe but now useless. //case '^': // return Token(TK_OP_BIT_XOR); //case '~': // return Token(TK_OP_BIT_INVERT); case '&': { if (GETCHAR(1)=='&') { r_chars++; return Token(TK_OP_AND); } return Token(TK_ERROR,"Unknown character"); /* if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_ASSIGN_BIT_AND); } else if (GETCHAR(1)=='&') { r_chars++; return Token(TK_OP_AND); } return TK_OP_BIT_AND;*/ } break; case '|': { if (GETCHAR(1)=='|') { r_chars++; return Token(TK_OP_OR); } return Token(TK_ERROR,"Unknown character"); /* if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_ASSIGN_BIT_OR); } else if (GETCHAR(1)=='|') { r_chars++; return Token(TK_OP_OR); } return TK_OP_BIT_OR; */ } break; case '*': { if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_ASSIGN_MUL); } return TK_OP_MUL; } break; case '+': { if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_ASSIGN_ADD); } /*else if (GETCHAR(1)=='+') { r_chars++; return Token(TK_OP_PLUS_PLUS); }*/ return TK_OP_ADD; } break; case '-': { if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_ASSIGN_SUB); }/* else if (GETCHAR(1)=='-') { r_chars++; return Token(TK_OP_MINUS_MINUS); }*/ return TK_OP_SUB; } break; /*case '%': { if (GETCHAR(1)=='=') { r_chars++; return Token(TK_OP_ASSIGN_MOD); } return TK_OP_MOD; } break;*/ default: { 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; String str; int i=0; while(true) { if (GETCHAR(i)=='.') { if (period_found || exponent_found) return Token(TK_ERROR,"Invalid numeric constant"); period_found=true; } else if (GETCHAR(i)=='x') { if (hexa_found || str.length()!=1 || str[0]!='0') return Token(TK_ERROR,"Invalid numeric constant"); hexa_found=true; } else if (GETCHAR(i)=='e') { if (hexa_found || exponent_found) return 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 Token(TK_ERROR,"Invalid numeric constant"); sign_found=true; } else break; str+=CharType(GETCHAR(i)); i++; } if (!_is_number(str[str.length()-1])) return Token(TK_ERROR,"Invalid numeric constant"); r_chars+=str.length()-1; return Token(TK_REAL_CONSTANT,str); /* if (period_found) return Token(TK_NUMBER_REAL,str); else return Token(TK_NUMBER_INTEGER,str);*/ } if (GETCHAR(0)=='.') { //parse period return Token(TK_PERIOD); } if (_is_text_char(GETCHAR(0))) { // parse identifier String str; str+=CharType(GETCHAR(0)); while(_is_text_char(GETCHAR(r_chars))) { str+=CharType(GETCHAR(r_chars)); r_chars++; } //see if keyword 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_INT,"int"}, {TK_TYPE_INT2,"int2"}, {TK_TYPE_INT3,"int3"}, {TK_TYPE_INT4,"int4"},*/ {TK_TYPE_FLOAT,"float"}, /*{TK_TYPE_FLOAT2,"float2"}, {TK_TYPE_FLOAT3,"float3"}, {TK_TYPE_FLOAT4,"float4"},*/ {TK_TYPE_VEC2,"vec2"}, {TK_TYPE_VEC3,"vec3"}, {TK_TYPE_VEC4,"vec4"}, {TK_TYPE_TEXTURE,"texture"}, {TK_TYPE_CUBEMAP,"cubemap"}, {TK_TYPE_COLOR,"color"}, /* {TK_TYPE_MAT2,"mat2"}, {TK_TYPE_MAT3,"mat3"}, {TK_TYPE_MAT4,"mat3"},*/ {TK_TYPE_MAT3,"mat3"}, {TK_TYPE_MAT4,"mat4"}, {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_BREAK,"break"}, {TK_CF_CONTINUE,"continue"},*/ {TK_CF_RETURN,"return"}, {TK_UNIFORM,"uniform"}, {TK_ERROR,NULL} }; int idx=0; while(keyword_list[idx].text) { if (str==keyword_list[idx].text) return Token(keyword_list[idx].token); idx++; } return Token(TK_INDENTIFIER,str); } return Token(TK_ERROR,"Unknown character"); } break; } ERR_PRINT("BUG"); return Token(); } Error ShaderLanguage::tokenize(const String& p_text,Vector *p_tokens,String *r_error,int *r_err_line,int *r_err_column) { int len =p_text.length(); int pos=0; int line=0; while(pospush_back(t); //if (prev_line!=line) // p_tokens->push_back(Token(TK_LINE,itos(line))); pos+=advance; } return OK; } String ShaderLanguage::lex_debug(const String& p_code) { Vector tokens; String error; int errline,errcol; if (tokenize(p_code,&tokens,&error,&errline,&errcol)!=OK) return error; String ret; for(int i=0;itype==Node::TYPE_BLOCK) { BlockNode *block = (BlockNode*)node; if (block->variables.has(p_identifier)) return true; } else if (node->type==Node::TYPE_PROGRAM) { ProgramNode *program = (ProgramNode*)node; for(int i=0;ifunctions.size();i++) { if (program->functions[i].name==p_identifier) { return true; } } if(program->builtin_variables.has(p_identifier)) { return true; } if(program->uniforms.has(p_identifier)) { return true; } } else if (node->type==Node::TYPE_FUNCTION) { FunctionNode *func=(FunctionNode*)node; for(int i=0;iarguments.size();i++) if (func->arguments[i].name==p_identifier) return true; } node=node->parent; } // try keywords int idx=0; //todo optimize while(intrinsic_func_defs[idx].name) { if (p_identifier.operator String()==intrinsic_func_defs[idx].name) return true; idx++; } return false; } Error ShaderLanguage::parse_function(Parser& parser,BlockNode *p_block) { if (!p_block->parent || p_block->parent->type!=Node::TYPE_PROGRAM) { parser.set_error("Misplaced function"); return ERR_PARSE_ERROR; } ProgramNode *program = (ProgramNode*)p_block->parent; StringName name = parser.get_token(1).text; if (test_existing_identifier(p_block,name)) { parser.set_error("Duplicate Identifier (existing variable/builtin/function): "+name); return ERR_PARSE_ERROR; } FunctionNode *function = parser.create_node(program); function->body = parser.create_node(function); function->name=name; function->return_type=get_token_datatype(parser.get_token_type(0)); { //add to programnode ProgramNode::Function f; f.name=name; f.function=function; program->functions.push_back(f); } int ofs=3; while(true) { //end of arguments if (parser.get_token_type(ofs)==TK_PARENTHESIS_CLOSE) { ofs++; break; } //next argument awaits if (parser.get_token_type(ofs)==TK_COMMA) { if (!is_token_nonvoid_datatype(parser.get_token_type(ofs+1))) { parser.set_error("Expected Identifier or ')' following ','"); return ERR_PARSE_ERROR; } ofs++; continue; } if (!is_token_nonvoid_datatype(parser.get_token_type(ofs+0))) { parser.set_error("Invalid Argument Type"); return ERR_PARSE_ERROR; } DataType identtype=get_token_datatype(parser.get_token_type(ofs+0)); if (parser.get_token_type(ofs+1)!=TK_INDENTIFIER) { parser.set_error("Expected Argument Identifier"); return ERR_PARSE_ERROR; } StringName identname=parser.get_token(ofs+1).text; if (test_existing_identifier(function,identname)) { parser.set_error("Duplicate Argument Identifier: "+identname); return ERR_DUPLICATE_SYMBOL; } FunctionNode::Argument arg; arg.name=identname; arg.type=identtype; //function->body->variables[arg.name]=arg.type; function->arguments.push_back(arg); ofs+=2; } parser.advance(ofs); // match { if (parser.get_token_type()!=TK_CURLY_BRACKET_OPEN) { parser.set_error("Expected '{'"); return ERR_PARSE_ERROR; } parser.advance(); Error err = parse_block(parser,function->body); if (err) return err; // make sure that if the function has a return type, it does return something.. if (function->return_type!=TYPE_VOID) { bool found=false; for(int i=0;ibody->statements.size();i++) { if (function->body->statements[i]->type==Node::TYPE_CONTROL_FLOW) { ControlFlowNode *cf = (ControlFlowNode*)function->body->statements[i]; if (cf->flow_op==FLOW_OP_RETURN) { // type of return was already checked when inserted // no need to check here found=true; } } } if (!found) { parser.set_error("Function must return a value (use the main block)"); return ERR_PARSE_ERROR; } } return OK; } const ShaderLanguage::IntrinsicFuncDef ShaderLanguage::intrinsic_func_defs[]={ //constructors {"bool",TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_FLOAT,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_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}}, {"mat3",TYPE_MAT3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mat4",TYPE_MAT4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,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}}, {"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}}, {"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_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mod",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}}, {"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}}, {"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}}, {"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}}, //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}}, //intrinsics - texture {"tex",TYPE_VEC4,{TYPE_TEXTURE,TYPE_VEC2,TYPE_VOID}}, {"texcube",TYPE_VEC4,{TYPE_CUBEMAP,TYPE_VEC3,TYPE_VOID}}, {"texscreen",TYPE_VEC3,{TYPE_VEC2,TYPE_VOID}}, {"texpos",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {NULL,TYPE_VOID,{TYPE_VOID}} }; const ShaderLanguage::OperatorDef ShaderLanguage::operator_defs[]={ {OP_ASSIGN,TYPE_VOID,{TYPE_BOOL,TYPE_BOOL}}, {OP_ASSIGN,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_ASSIGN,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}}, {OP_ASSIGN,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}}, {OP_ASSIGN,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}}, {OP_ASSIGN,TYPE_VOID,{TYPE_MAT3,TYPE_MAT3}}, {OP_ASSIGN,TYPE_VOID,{TYPE_MAT4,TYPE_MAT4}}, {OP_ADD,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_ADD,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}}, {OP_ADD,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}}, {OP_ADD,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}}, {OP_SUB,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_SUB,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}}, {OP_SUB,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}}, {OP_SUB,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}}, {OP_MUL,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}}, {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT}}, {OP_MUL,TYPE_VEC2,{TYPE_FLOAT,TYPE_VEC2}}, {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_MAT3}}, {OP_MUL,TYPE_VEC2,{TYPE_MAT3,TYPE_VEC2}}, {OP_MUL,TYPE_VEC2,{TYPE_VEC2,TYPE_MAT4}}, {OP_MUL,TYPE_VEC2,{TYPE_MAT4,TYPE_VEC2}}, {OP_MUL,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}}, {OP_MUL,TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT}}, {OP_MUL,TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC3}}, {OP_MUL,TYPE_VEC3,{TYPE_MAT3,TYPE_VEC3}}, {OP_MUL,TYPE_VEC3,{TYPE_MAT4,TYPE_VEC3}}, {OP_MUL,TYPE_VEC3,{TYPE_VEC3,TYPE_MAT3}}, {OP_MUL,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}}, {OP_MUL,TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT}}, {OP_MUL,TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC4}}, {OP_MUL,TYPE_VEC4,{TYPE_MAT4,TYPE_VEC4}}, {OP_MUL,TYPE_VEC4,{TYPE_VEC4,TYPE_MAT4}}, {OP_MUL,TYPE_MAT3,{TYPE_MAT3,TYPE_MAT3}}, {OP_MUL,TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4}}, {OP_DIV,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_DIV,TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2}}, {OP_DIV,TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT}}, {OP_DIV,TYPE_VEC2,{TYPE_FLOAT,TYPE_VEC2}}, {OP_DIV,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}}, {OP_DIV,TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT}}, {OP_DIV,TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC3}}, {OP_DIV,TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4}}, {OP_DIV,TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT}}, {OP_DIV,TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC4}}, {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}}, {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}}, {OP_ASSIGN_ADD,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}}, {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}}, {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}}, {OP_ASSIGN_SUB,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC2,TYPE_FLOAT}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT3,TYPE_VEC2}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_VEC2}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC3,TYPE_FLOAT}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT3,TYPE_VEC3}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_VEC3}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_VEC4,TYPE_FLOAT}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_VEC4}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT3,TYPE_MAT3}}, {OP_ASSIGN_MUL,TYPE_VOID,{TYPE_MAT4,TYPE_MAT4}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC2,TYPE_VEC2}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC2,TYPE_FLOAT}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC3,TYPE_VEC3}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC3,TYPE_FLOAT}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC4,TYPE_VEC4}}, {OP_ASSIGN_DIV,TYPE_VOID,{TYPE_VEC4,TYPE_FLOAT}}, {OP_NEG,TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {OP_NEG,TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {OP_NEG,TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {OP_NEG,TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {OP_NOT,TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}}, {OP_CMP_EQ,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}}, {OP_CMP_EQ,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_CMP_EQ,TYPE_VEC2,{TYPE_VEC3,TYPE_VEC2}}, {OP_CMP_EQ,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}}, {OP_CMP_EQ,TYPE_VEC4,{TYPE_VEC3,TYPE_VEC4}}, //{OP_CMP_EQ,TYPE_MAT3,{TYPE_MAT4,TYPE_MAT3}}, ?? //{OP_CMP_EQ,TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4}}, ?? {OP_CMP_NEQ,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}}, {OP_CMP_NEQ,TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_CMP_NEQ,TYPE_VEC3,{TYPE_VEC2,TYPE_VEC2}}, {OP_CMP_NEQ,TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3}}, {OP_CMP_NEQ,TYPE_VEC3,{TYPE_VEC4,TYPE_VEC4}}, //{OP_CMP_NEQ,TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4}}, //? {OP_CMP_LEQ,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_CMP_GEQ,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_CMP_LESS,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_CMP_GREATER,TYPE_BOOL,{TYPE_FLOAT,TYPE_FLOAT}}, {OP_CMP_OR,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}}, {OP_CMP_AND,TYPE_BOOL,{TYPE_BOOL,TYPE_BOOL}}, {OP_MAX,TYPE_VOID,{TYPE_VOID,TYPE_VOID}} }; const ShaderLanguage::BuiltinsDef ShaderLanguage::vertex_builtins_defs[]={ { "VERTEX", TYPE_VEC3}, { "NORMAL", TYPE_VEC3}, { "TANGENT", TYPE_VEC3}, { "BINORMAL", TYPE_VEC3}, { "UV", TYPE_VEC2}, { "UV2", TYPE_VEC2}, { "COLOR", TYPE_VEC4}, { "BONES", TYPE_VEC4}, { "WEIGHTS", TYPE_VEC4}, { "VAR1", TYPE_VEC4}, { "VAR2", TYPE_VEC4}, { "SPEC_EXP", TYPE_FLOAT}, { "POINT_SIZE", TYPE_FLOAT}, //builtins { "WORLD_MATRIX", TYPE_MAT4}, { "INV_CAMERA_MATRIX", TYPE_MAT4}, { "PROJECTION_MATRIX", TYPE_MAT4}, { "INSTANCE_ID", TYPE_FLOAT}, { "TIME", TYPE_FLOAT}, { NULL, TYPE_VOID}, }; const ShaderLanguage::BuiltinsDef ShaderLanguage::fragment_builtins_defs[]={ { "VERTEX", TYPE_VEC3}, { "POSITION", TYPE_VEC3}, { "NORMAL", TYPE_VEC3}, { "TANGENT", TYPE_VEC3}, { "BINORMAL", TYPE_VEC3}, { "UV", TYPE_VEC2}, { "UV2", TYPE_VEC2}, { "COLOR", TYPE_VEC4}, { "NORMAL", TYPE_VEC3}, { "VAR1", TYPE_VEC4}, { "VAR2", TYPE_VEC4}, { "DIFFUSE", TYPE_VEC3}, { "DIFFUSE_ALPHA", TYPE_VEC4}, { "SPECULAR", TYPE_VEC3}, { "EMISSION", TYPE_VEC3}, { "SPEC_EXP", TYPE_FLOAT}, { "GLOW", TYPE_FLOAT}, { "SHADE_PARAM", TYPE_FLOAT}, { "DISCARD", TYPE_BOOL}, { "SCREEN_UV", TYPE_VEC2}, { "POINT_COORD", TYPE_VEC2}, { "INV_CAMERA_MATRIX", TYPE_MAT4}, // { "SCREEN_POS", TYPE_VEC2}, // { "SCREEN_TEXEL_SIZE", TYPE_VEC2}, { "TIME", TYPE_FLOAT}, { NULL, TYPE_VOID} }; const ShaderLanguage::BuiltinsDef ShaderLanguage::light_builtins_defs[]={ { "NORMAL", TYPE_VEC3}, { "LIGHT_DIR", TYPE_VEC3}, { "LIGHT_DIFFUSE", TYPE_VEC3}, { "LIGHT_SPECULAR", TYPE_VEC3}, { "EYE_VEC", TYPE_VEC3}, { "DIFFUSE", TYPE_VEC3}, { "SPECULAR", TYPE_VEC3}, { "SPECULAR_EXP", TYPE_FLOAT}, { "SHADE_PARAM", TYPE_FLOAT}, { "LIGHT", TYPE_VEC3}, { "POINT_COORD", TYPE_VEC2}, // { "SCREEN_POS", TYPE_VEC2}, // { "SCREEN_TEXEL_SIZE", TYPE_VEC2}, { "TIME", TYPE_FLOAT}, { NULL, TYPE_VOID} }; const ShaderLanguage::BuiltinsDef ShaderLanguage::postprocess_fragment_builtins_defs[]={ { "IN_COLOR", TYPE_VEC3}, { "IN_POSITION", TYPE_VEC3}, { "OUT_COLOR", TYPE_VEC3}, { "SCREEN_POS", TYPE_VEC2}, { "SCREEN_TEXEL_SIZE", TYPE_VEC2}, { "TIME", TYPE_FLOAT}, { NULL, TYPE_VOID} }; ShaderLanguage::DataType ShaderLanguage::compute_node_type(Node *p_node) { switch(p_node->type) { case Node::TYPE_PROGRAM: ERR_FAIL_V(TYPE_VOID); case Node::TYPE_FUNCTION: return static_cast(p_node)->return_type; case Node::TYPE_BLOCK: ERR_FAIL_V(TYPE_VOID); case Node::TYPE_VARIABLE: return static_cast(p_node)->datatype_cache; case Node::TYPE_CONSTANT: return static_cast(p_node)->datatype; case Node::TYPE_OPERATOR: return static_cast(p_node)->return_cache; case Node::TYPE_CONTROL_FLOW: ERR_FAIL_V(TYPE_VOID); case Node::TYPE_MEMBER: return static_cast(p_node)->datatype; } return TYPE_VOID; } ShaderLanguage::Node* ShaderLanguage::validate_function_call(Parser&parser, OperatorNode *p_func) { 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 ); String 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(compute_node_type(p_func->arguments[i])); } 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;ireturn_cache=intrinsic_func_defs[idx].rettype; found_intrinsic=true; break; } } idx++; } } if (found_intrinsic) { if (p_func->op==OP_CONSTRUCT && all_const) { bool all_const=false; 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::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: data = Vector2(cdata[0],cdata[1]); break; case TYPE_VEC3: data = Vector3(cdata[0],cdata[1],cdata[2]); break; case TYPE_VEC4: 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; } // try existing functions.. FunctionNode *exclude_function=NULL; //exclude current function (in case inside one) Node *node = p_func; while(node->parent) { if (node->type==Node::TYPE_FUNCTION) { exclude_function = (FunctionNode*)node; } node=node->parent; } ERR_FAIL_COND_V(node->type!=Node::TYPE_PROGRAM,NULL); ProgramNode *program = (ProgramNode*)node; for(int i=0;ifunctions.size();i++) { if (program->functions[i].function==exclude_function) continue; FunctionNode *pfunc = program->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 p_func; } } return NULL; } ShaderLanguage::Node * ShaderLanguage::validate_operator(Parser& parser,OperatorNode *p_func) { int argcount = p_func->arguments.size(); ERR_FAIL_COND_V(argcount>2,NULL); DataType argtype[2]={TYPE_VOID,TYPE_VOID}; bool all_const=true; for(int i=0;iarguments[i]); if (p_func->arguments[i]->type!=Node::TYPE_CONSTANT) all_const=false; } int idx=0; bool valid=false; while(operator_defs[idx].op!=OP_MAX) { if (p_func->op==operator_defs[idx].op) { if (operator_defs[idx].args[0]==argtype[0] && operator_defs[idx].args[1]==argtype[1]) { p_func->return_cache=operator_defs[idx].rettype; valid=true; break; } } idx++; } if (!valid) return NULL; #define _RCO2(m_op,m_vop)\ case m_op: {\ ConstantNode *cn = parser.create_node(p_func->parent);\ cn->datatype=p_func->return_cache; \ Variant::evaluate(m_vop,static_cast(p_func->arguments[0])->value,static_cast(p_func->arguments[1])->value,cn->value,valid);\ if (!valid)\ return NULL;\ return cn;\ } break; #define _RCO1(m_op,m_vop)\ case m_op: {\ ConstantNode *cn = parser.create_node(p_func->parent);\ cn->datatype=p_func->return_cache; \ Variant::evaluate(m_vop,static_cast(p_func->arguments[0])->value,Variant(),cn->value,valid);\ if (!valid)\ return NULL;\ return cn;\ } break; if (all_const) { //reduce constant operator switch(p_func->op) { _RCO2(OP_ADD,Variant::OP_ADD); _RCO2(OP_SUB,Variant::OP_SUBSTRACT); _RCO2(OP_MUL,Variant::OP_MULTIPLY); _RCO2(OP_DIV,Variant::OP_DIVIDE); _RCO1(OP_NEG,Variant::OP_NEGATE); _RCO1(OP_NOT,Variant::OP_NOT); _RCO2(OP_CMP_EQ,Variant::OP_EQUAL); _RCO2(OP_CMP_NEQ,Variant::OP_NOT_EQUAL); _RCO2(OP_CMP_LEQ,Variant::OP_LESS_EQUAL); _RCO2(OP_CMP_GEQ,Variant::OP_GREATER_EQUAL); _RCO2(OP_CMP_LESS,Variant::OP_LESS); _RCO2(OP_CMP_GREATER,Variant::OP_GREATER); _RCO2(OP_CMP_OR,Variant::OP_OR); _RCO2(OP_CMP_AND,Variant::OP_AND); default: {} } } return p_func; } 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_NEG) || (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); } ShaderLanguage::Operator ShaderLanguage::get_token_operator(TokenType p_type) { switch(p_type) { case TK_OP_EQUAL: return OP_CMP_EQ ; case TK_OP_NOT_EQUAL: return OP_CMP_NEQ; case TK_OP_LESS: return OP_CMP_LESS ; case TK_OP_LESS_EQUAL: return OP_CMP_LEQ ; case TK_OP_GREATER: return OP_CMP_GREATER ; case TK_OP_GREATER_EQUAL: return OP_CMP_GEQ ; case TK_OP_AND: return OP_CMP_AND ; case TK_OP_OR: return OP_CMP_OR ; case TK_OP_NOT: return OP_NOT ; case TK_OP_ADD: return OP_ADD ; case TK_OP_SUB: return OP_SUB ; case TK_OP_MUL: return OP_MUL ; case TK_OP_DIV: return OP_DIV ; case TK_OP_NEG: return OP_NEG ; case TK_OP_ASSIGN: return OP_ASSIGN ; case TK_OP_ASSIGN_ADD: return OP_ASSIGN_ADD ; case TK_OP_ASSIGN_SUB: return OP_ASSIGN_SUB ; case TK_OP_ASSIGN_MUL: return OP_ASSIGN_MUL ; case TK_OP_ASSIGN_DIV: return OP_ASSIGN_DIV ; default: ERR_FAIL_V(OP_MAX); } return OP_MAX; } Error ShaderLanguage::parse_expression(Parser& parser,Node *p_parent,Node **r_expr) { Vector expressions; Vector operators; while(true) { Node *expr=NULL; if (parser.get_token_type()==TK_PARENTHESIS_OPEN) { //handle subexpression parser.advance(); Error err = parse_expression(parser,p_parent,&expr); if (err) return err; if (parser.get_token_type()!=TK_PARENTHESIS_CLOSE) { parser.set_error("Expected ')' in expression"); return ERR_PARSE_ERROR; } parser.advance(); } else if (parser.get_token_type()==TK_REAL_CONSTANT) { ConstantNode *constant = parser.create_node(p_parent); constant->value=parser.get_token().text.operator String().to_double(); constant->datatype=TYPE_FLOAT; expr=constant; parser.advance(); } else if (parser.get_token_type()==TK_TRUE) { //print_line("found true"); //handle true constant ConstantNode *constant = parser.create_node(p_parent); constant->value=true; constant->datatype=TYPE_BOOL; expr=constant; parser.advance(); } else if (parser.get_token_type()==TK_FALSE) { //handle false constant ConstantNode *constant = parser.create_node(p_parent); constant->value=false; constant->datatype=TYPE_BOOL; expr=constant; parser.advance(); } else if (parser.get_token_type()==TK_TYPE_VOID) { //make sure void is not used in expression parser.set_error("Void value not allowed in Expression"); return ERR_PARSE_ERROR; } else if (parser.get_token_type(1)==TK_PARENTHESIS_OPEN && (is_token_nonvoid_datatype(parser.get_token_type()) || parser.get_token_type()==TK_INDENTIFIER)) { //function or constructor StringName name; DataType constructor=TYPE_VOID; if (is_token_nonvoid_datatype(parser.get_token_type())) { constructor=get_token_datatype(parser.get_token_type()); switch(get_token_datatype(parser.get_token_type())) { case TYPE_BOOL: name="bool"; break; case TYPE_FLOAT: name="float"; break; case TYPE_VEC2: name="vec2"; break; case TYPE_VEC3: name="vec3"; break; case TYPE_VEC4: name="vec4"; break; case TYPE_MAT3: name="mat3"; break; case TYPE_MAT4: name="mat4"; break; default: ERR_FAIL_V(ERR_BUG); } } else { name=parser.get_token().text; } if (!test_existing_identifier(p_parent,name)) { parser.set_error("Unknown identifier in expression: "+name); return ERR_PARSE_ERROR; } parser.advance(2); OperatorNode *func = parser.create_node(p_parent); func->op=constructor!=TYPE_VOID?OP_CONSTRUCT:OP_CALL; VariableNode *funcname = parser.create_node(func); funcname->name=name; func->arguments.push_back(funcname); //parse parameters if (parser.get_token_type()==TK_PARENTHESIS_CLOSE) { parser.advance(); } else { while(true) { Node *arg=NULL; Error err = parse_expression(parser,func,&arg); if (err) return err; func->arguments.push_back(arg); if (parser.get_token_type()==TK_PARENTHESIS_CLOSE) { parser.advance(); break; } else if (parser.get_token_type()==TK_COMMA) { if (parser.get_token_type(1)==TK_PARENTHESIS_CLOSE) { parser.set_error("Expression expected"); return ERR_PARSE_ERROR; } parser.advance(); } else { // something is broken parser.set_error("Expected ',' or ')'"); return ERR_PARSE_ERROR; } } } expr=validate_function_call(parser,func); if (!expr) { parser.set_error("Invalid arguments to function/constructor: "+StringName(name)); return ERR_PARSE_ERROR; } } else if (parser.get_token_type()==TK_INDENTIFIER) { //probably variable Node *node =p_parent; bool existing=false; DataType datatype; StringName identifier=parser.get_token().text; while(node) { if (node->type==Node::TYPE_BLOCK) { BlockNode *block = (BlockNode*)node; if (block->variables.has(identifier)) { existing=true; datatype=block->variables[identifier]; break; } } if (node->type==Node::TYPE_FUNCTION) { FunctionNode *function=(FunctionNode*)node; for(int i=0;iarguments.size();i++) { if (function->arguments[i].name==identifier) { existing=true; datatype=function->arguments[i].type; break; } } if (existing) break; } if (node->type==Node::TYPE_PROGRAM) { ProgramNode *program = (ProgramNode*)node; if (program->builtin_variables.has(identifier)) { datatype = program->builtin_variables[identifier]; existing=true; break; } if (program->uniforms.has(identifier)) { datatype = program->uniforms[identifier].type; existing=true; break; } } node=node->parent; } if (!existing) { parser.set_error("Unexisting identifier in expression: "+identifier); return ERR_PARSE_ERROR; } VariableNode *varname = parser.create_node(p_parent); varname->name=identifier; varname->datatype_cache=datatype; parser.advance(); expr=varname; } else if (parser.get_token_type()==TK_OP_NEG || parser.get_token_type()==TK_OP_NOT) { //single prefix operators TokenType token_type=parser.get_token_type(); parser.advance(); Node *subexpr=NULL; Error err = parse_expression(parser,p_parent,&subexpr); if (err) return err; OperatorNode *op = parser.create_node(p_parent); switch(token_type) { case TK_OP_NEG: op->op=OP_NEG; break; case TK_OP_NOT: op->op=OP_NOT; break; //case TK_OP_PLUS_PLUS: op->op=OP_PLUS_PLUS; break; //case TK_OP_MINUS_MINUS: op->op=OP_MINUS_MINUS; break; default: ERR_FAIL_V(ERR_BUG); } op->arguments.push_back(subexpr); expr=validate_operator(parser,op); if (!expr) { parser.set_error("Invalid argument for negation operator"); return ERR_PARSE_ERROR; } } else { print_line("found bug?"); print_line("misplaced token: "+String(token_names[parser.get_token_type()])); parser.set_error("Error parsing expression, misplaced: "+String(token_names[parser.get_token_type()])); return ERR_PARSE_ERROR; //nothing } ERR_FAIL_COND_V(!expr,ERR_BUG); /* 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.. */ if (parser.get_token_type()==TK_PERIOD) { if (parser.get_token_type(1)!=TK_INDENTIFIER) { parser.set_error("Expected identifier as member"); return ERR_PARSE_ERROR; } DataType dt = compute_node_type(expr); String ident = parser.get_token(1).text; bool ok=true; DataType member_type; switch(dt) { case TYPE_VEC2: { int l = ident.length(); if (l==1) { member_type=TYPE_FLOAT; } else if (l==2) { member_type=TYPE_VEC2; } else { ok=false; break; } const CharType *c=ident.ptr(); for(int i=0;i(p_parent); mn->basetype=dt; mn->datatype=member_type; mn->name=ident; mn->owner=expr; expr=mn; parser.advance(2); //todo //member (period) has priority over any operator //creates a subindexing expression in place } else if (parser.get_token_type()==TK_BRACKET_OPEN) { //todo //subindexing has priority over any operator //creates a subindexing expression in place } /*else if (parser.get_token_type()==TK_OP_PLUS_PLUS || parser.get_token_type()==TK_OP_MINUS_MINUS) { //todo //inc/dec operators have priority over any operator //creates a subindexing expression in place //return OK; //wtfs } */ expressions.push_back(expr); if (is_token_operator(parser.get_token_type())) { operators.push_back(parser.get_token_type()); parser.advance(); } else { break; } } ERR_FAIL_COND_V(expressions.size()!=(operators.size()+1),ERR_BUG); /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ while(expressions.size()>1) { int next_op=-1; int min_priority=0xFFFFF; for(int i=0;i(p_parent); op->op=get_token_operator(operators[next_op]); op->arguments.push_back(expressions[next_op]); //expression goes as left op->arguments.push_back(expressions[next_op+1]); //next expression goes as right expressions[next_op]=validate_operator(parser,op); if (!expressions[next_op]) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(compute_node_type(op->arguments[i])); } parser.set_error("Invalid arguments to operator "+String(token_names[operators[next_op]])+": "+at); return ERR_PARSE_ERROR; } expressions.remove(next_op+1); operators.remove(next_op); } *r_expr=expressions[0]; return OK; /* TokenType token_type=parser.get_token_type(); OperatorNode *op = parser.create_node(p_parent); op->op=get_token_operator(parser.get_token_type()); op->arguments.push_back(*r_expr); //expression goes as left parser.advance(); Node *right_expr=NULL; Error err = parse_expression(parser,p_parent,&right_expr); if (err) return err; op->arguments.push_back(right_expr); if (!validate_operator(op)) { parser.set_error("Invalid arguments to operator "+String(token_names[token_type])); return ERR_PARSE_ERROR; } */ } Error ShaderLanguage::parse_variable_declaration(Parser& parser,BlockNode *p_block) { bool uniform = parser.get_token(-1).type==TK_UNIFORM; DataType type=get_token_datatype(parser.get_token_type(0)); bool iscolor = parser.get_token_type(0)==TK_TYPE_COLOR; if (type==TYPE_VOID) { parser.set_error("Cannot Declare a 'void' Variable"); return ERR_PARSE_ERROR; } if (type==TYPE_TEXTURE && !uniform) { parser.set_error("Cannot Declare a Non-Uniform Texture"); return ERR_PARSE_ERROR; } if (type==TYPE_CUBEMAP && !uniform) { parser.set_error("Cannot Declare a Non-Uniform Cubemap"); return ERR_PARSE_ERROR; } parser.advance(); int found=0; while(true) { if (found && parser.get_token_type()!=TK_COMMA) { break; } if (parser.get_token_type()!=TK_INDENTIFIER) { parser.set_error("Identifier Expected"); return ERR_PARSE_ERROR; } StringName name = parser.get_token().text; if (test_existing_identifier(p_block,name)) { parser.set_error("Duplicate Identifier (existing variable/function): "+name); return ERR_PARSE_ERROR; } found=true; parser.advance(); //see if declaration has an initializer if (parser.get_token_type()==TK_OP_ASSIGN) { parser.advance(); OperatorNode * op = parser.create_node(p_block); VariableNode * var = parser.create_node(op); var->name=name; var->datatype_cache=type; var->uniform=uniform; Node *expr; Error err = parse_expression(parser,p_block,&expr); if (err) return err; if (var->uniform) { if (expr->type!=Node::TYPE_CONSTANT) { parser.set_error("Uniform can only be initialized to a constant."); return ERR_PARSE_ERROR; } Uniform u; u.order=parser.program->uniforms.size(); u.type=type; u.default_value=static_cast(expr)->value; if (iscolor && u.default_value.get_type()==Variant::PLANE) { Color c; Plane p = u.default_value; c=Color(p.normal.x,p.normal.y,p.normal.z,p.d); u.default_value=c; } parser.program->uniforms[var->name]=u; } else { op->op=OP_ASSIGN; op->arguments.push_back(var); op->arguments.push_back(expr); Node *n=validate_operator(parser,op); if (!n) { parser.set_error("Invalid initializer for variable: "+name); return ERR_PARSE_ERROR; } p_block->statements.push_back(n); } } else { //initialize it EMPTY OperatorNode * op = parser.create_node(p_block); VariableNode * var = parser.create_node(op); ConstantNode * con = parser.create_node(op); var->name=name; var->datatype_cache=type; var->uniform=uniform; con->datatype=type; switch(type) { case TYPE_BOOL: con->value=false; break; case TYPE_FLOAT: con->value=0.0; break; case TYPE_VEC2: con->value=Vector2(); break; case TYPE_VEC3: con->value=Vector3(); break; case TYPE_VEC4: con->value=iscolor?Variant(Color()):Variant(Plane()); break; case TYPE_MAT3: con->value=Matrix3(); break; case TYPE_MAT4: con->value=Transform(); break; case TYPE_TEXTURE: case TYPE_CUBEMAP: con->value=RID(); break; default: {} } if (uniform) { Uniform u; u.type=type; u.default_value=con->value; u.order=parser.program->uniforms.size(); parser.program->uniforms[var->name]=u; } else { op->op=OP_ASSIGN; op->arguments.push_back(var); op->arguments.push_back(con); p_block->statements.push_back(op); } } if (!uniform) p_block->variables[name]=type; } if (parser.get_token_type()!=TK_SEMICOLON) { parser.set_error("Expected ';'"); return ERR_PARSE_ERROR; } return OK; } Error ShaderLanguage::parse_flow_if(Parser& parser,Node *p_parent,Node **r_statement) { ControlFlowNode *cf = parser.create_node(p_parent); cf->flow_op=FLOW_OP_IF; parser.advance(); if (parser.get_token_type()!=TK_PARENTHESIS_OPEN) { parser.set_error("Expected '(' after 'if'"); return ERR_PARSE_ERROR; } parser.advance(); Node *expression=NULL; Error err = parse_expression(parser,cf,&expression); if (err) return err; if (compute_node_type(expression)!=TYPE_BOOL) { parser.set_error("Expression for 'if' is not boolean"); return ERR_PARSE_ERROR; } cf->statements.push_back(expression); if (parser.get_token_type()!=TK_PARENTHESIS_CLOSE) { parser.set_error("Expected ')' after expression"); return ERR_PARSE_ERROR; } parser.advance(); Node *substatement=NULL; err = parse_statement(parser,cf,&substatement); if (err) return err; cf->statements.push_back(substatement); if (parser.get_token_type()==TK_CF_ELSE) { parser.advance(); substatement=NULL; err = parse_statement(parser,cf,&substatement); if (err) return err; cf->statements.push_back(substatement); } *r_statement=cf; return OK; } Error ShaderLanguage::parse_flow_return(Parser& parser,Node *p_parent,Node **r_statement) { FunctionNode *function=NULL; Node *parent=p_parent; while(parent) { if (parent->type==Node::TYPE_FUNCTION) { function=(FunctionNode*)parent; break; } parent=parent->parent; } if (!function) { parser.set_error("'return' must be inside a function"); return ERR_PARSE_ERROR; } ControlFlowNode *cf = parser.create_node(p_parent); cf->flow_op=FLOW_OP_RETURN; parser.advance(); if (function->return_type!=TYPE_VOID) { // should expect a return expression. Node *expr=NULL; Error err = parse_expression(parser,cf,&expr); if (err) return err; if (compute_node_type(expr)!=function->return_type) { parser.set_error("Invalid type for 'return' expression"); return ERR_PARSE_ERROR; } cf->statements.push_back(expr); } *r_statement=cf; if (parser.get_token_type()!=TK_SEMICOLON) { parser.set_error("Expected ';'"); return ERR_PARSE_ERROR; } return OK; } Error ShaderLanguage::parse_statement(Parser& parser,Node *p_parent,Node **r_statement) { *r_statement=NULL; TokenType token_type = parser.get_token_type(); if (token_type==TK_CURLY_BRACKET_OPEN) { //sub-block parser.advance(); BlockNode *block = parser.create_node(p_parent); *r_statement=block; return parse_block(parser,block); } else if (token_type==TK_SEMICOLON) { // empty ; parser.advance(); return OK; } else if (token_type==TK_CF_IF) { return parse_flow_if(parser,p_parent,r_statement); } else if (token_type==TK_CF_RETURN) { return parse_flow_return(parser,p_parent,r_statement); } else { Error err=parse_expression(parser,p_parent,r_statement); if (err) return err; if (parser.get_token_type()!=TK_SEMICOLON) { parser.set_error("Expected ';'"); return ERR_PARSE_ERROR; } } return OK; } Error ShaderLanguage::parse_block(Parser& parser,BlockNode *p_block) { while(true) { if (parser.is_at_end()) { if (p_block->parent->type!=Node::TYPE_PROGRAM) { parser.set_error("Unexpected End of File"); return ERR_PARSE_ERROR; } return OK; //bye } TokenType token_type = parser.get_token_type(); if (token_type==TK_CURLY_BRACKET_CLOSE) { if (p_block->parent->type==Node::TYPE_PROGRAM) { parser.set_error("Unexpected '}'"); return ERR_PARSE_ERROR; } parser.advance(); return OK; // exit block } else if (token_type==TK_UNIFORM) { if (p_block!=parser.program->body) { parser.set_error("Uniform only allowed in main program body."); return ERR_PARSE_ERROR; } parser.advance(); Error err=parse_variable_declaration(parser,p_block); if (err) return err; } else if (is_token_datatype(token_type)) { Error err=OK; if (parser_is_at_function(parser)) err = parse_function(parser,p_block); else { err = parse_variable_declaration(parser,p_block); } if (err) return err; } else { // must be a statement Node *statement=NULL; Error err = parse_statement(parser,p_block,&statement); if (err) return err; if (statement) { p_block->statements.push_back(statement); } } } return OK; } Error ShaderLanguage::parse(const Vector& p_tokens,ShaderType p_type,CompileFunc p_compile_func,void *p_userdata,String *r_error,int *r_err_line,int *r_err_column) { uint64_t t = OS::get_singleton()->get_ticks_usec(); Parser parser(p_tokens); parser.program = parser.create_node(NULL); parser.program->body = parser.create_node(parser.program); //add builtins switch(p_type) { case SHADER_MATERIAL_VERTEX: { int idx=0; while (vertex_builtins_defs[idx].name) { parser.program->builtin_variables[vertex_builtins_defs[idx].name]=vertex_builtins_defs[idx].type; idx++; } } break; case SHADER_MATERIAL_FRAGMENT: { int idx=0; while (fragment_builtins_defs[idx].name) { parser.program->builtin_variables[fragment_builtins_defs[idx].name]=fragment_builtins_defs[idx].type; idx++; } } break; case SHADER_MATERIAL_LIGHT: { int idx=0; while (light_builtins_defs[idx].name) { parser.program->builtin_variables[light_builtins_defs[idx].name]=light_builtins_defs[idx].type; idx++; } } break; case SHADER_POST_PROCESS: { int idx=0; while (postprocess_fragment_builtins_defs[idx].name) { parser.program->builtin_variables[postprocess_fragment_builtins_defs[idx].name]=postprocess_fragment_builtins_defs[idx].type; idx++; } } break; } Error err = parse_block(parser,parser.program->body); if (err) { parser.get_error(r_error,r_err_line,r_err_column); return err; } double tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0; //print_line("parse time: "+rtos(tf)); t = OS::get_singleton()->get_ticks_usec(); if (p_compile_func) { err = p_compile_func(p_userdata,parser.program); } tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0; //print_line("compile time: "+rtos(tf)); //clean up nodes created while(parser.nodegc.size()) { memdelete( parser.nodegc.front()->get() ); parser.nodegc.pop_front(); } return err; } Error ShaderLanguage::compile(const String& p_code,ShaderType p_type,CompileFunc p_compile_func,void *p_userdata,String *r_error,int *r_err_line,int *r_err_column) { *r_error=""; *r_err_line=0; *r_err_column=0; Vector tokens; uint64_t t = OS::get_singleton()->get_ticks_usec(); Error err = tokenize(p_code,&tokens,r_error,r_err_line,r_err_column); double tf = (OS::get_singleton()->get_ticks_usec()-t)/1000.0; //print_line("tokenize time: "+rtos(tf)); if (err!=OK) { return err; } err = parse(tokens,p_type,p_compile_func,p_userdata,r_error,r_err_line,r_err_column); if (err!=OK) { //print_line("LDEBUG: "+lex_debug(p_code)); return err; } return OK; } void ShaderLanguage::get_keyword_list(ShaderType p_type, List *p_keywords) { int idx=0; while(intrinsic_func_defs[idx].name) { p_keywords->push_back(intrinsic_func_defs[idx].name); idx++; } switch(p_type) { case SHADER_MATERIAL_VERTEX: { idx=0; while (vertex_builtins_defs[idx].name) { p_keywords->push_back(vertex_builtins_defs[idx].name); idx++; } } break; case SHADER_MATERIAL_FRAGMENT: { idx=0; while (fragment_builtins_defs[idx].name) { p_keywords->push_back(fragment_builtins_defs[idx].name); idx++; } } break; case SHADER_MATERIAL_LIGHT: { idx=0; while (light_builtins_defs[idx].name) { p_keywords->push_back(light_builtins_defs[idx].name); idx++; } } break; case SHADER_POST_PROCESS: { idx=0; while (postprocess_fragment_builtins_defs[idx].name) { p_keywords->push_back(postprocess_fragment_builtins_defs[idx].name); idx++; } } break; } }