From: Mikko Rasa Date: Mon, 23 Dec 2013 16:14:13 +0000 (+0200) Subject: More flexible system for customizing generated shaders X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=103cebacd5572e770e4463163688802ee4f6efbf;p=libs%2Fgl.git More flexible system for customizing generated shaders Any standard variable can now be overridden, and new ones added. It's still somewhat fragile concerning variable ordering and some other things, but it works when used correctly. --- diff --git a/source/program.cpp b/source/program.cpp index 50ebed22..baeba9f5 100644 --- a/source/program.cpp +++ b/source/program.cpp @@ -29,8 +29,7 @@ Program::Program(const ProgramBuilder::StandardFeatures &features) ProgramBuilder builder(features); builder.add_shaders(*this); - if(!features.transform) - link(); + link(); } Program::Program(const string &vert, const string &frag) diff --git a/source/programbuilder.cpp b/source/programbuilder.cpp index 44b7798e..29df27ef 100644 --- a/source/programbuilder.cpp +++ b/source/programbuilder.cpp @@ -39,7 +39,7 @@ Naming conventions: /* The array are stored in reverse order, so that variables always come after anything that might need them. */ -const ProgramBuilder::StandardVariable ProgramBuilder::standard_variables[] = +const ProgramBuilder::VariableDefinition ProgramBuilder::standard_variables[] = { { FRAGMENT, "gl_FragColor", 0, "frag_color", "g" }, { FRAGMENT, "frag_color", 0, "color_base", "!t" }, @@ -79,10 +79,8 @@ const ProgramBuilder::StandardVariable ProgramBuilder::standard_variables[] = { FRAGMENT, "n_zzz_light_dir", "vec3", "normalize(zzz_light_dir)", 0 }, { FRAGMENT, "n_tbn_normal", "vec3", "normal_sample*2.0-1.0", "n" }, { FRAGMENT, "n_eye_normal", "vec3", "normalize(eye_normal)", "!n" }, - { FRAGMENT, "normal_sample", "vec3", "texture2D(normalmap, texture_coord).xyz", "!c" }, - { FRAGMENT, "normal_sample", "vec3", "sample_normalmap(texture_coord)", "c" }, - { FRAGMENT, "tex_sample", "vec4", "texture2D(texture, texture_coord)", "!c" }, - { FRAGMENT, "tex_sample", "vec4", "sample_texture(texture_coord)", "c" }, + { FRAGMENT, "normal_sample", "vec3", "texture2D(normalmap, texture_coord).xyz", 0 }, + { FRAGMENT, "tex_sample", "vec4", "texture2D(texture, texture_coord)", 0 }, { VERTEX, "gl_Position", 0, "projection_matrix*eye_vertex", 0 }, { VERTEX, "shd_vertex", "vec3", "vec3(dot(eye_vertex, gl_EyePlaneS[shadow_unit]), dot(eye_vertex, gl_EyePlaneT[shadow_unit]), dot(eye_vertex, gl_EyePlaneR[shadow_unit]))", "g" }, @@ -93,14 +91,10 @@ const ProgramBuilder::StandardVariable ProgramBuilder::standard_variables[] = { VERTEX, "tbn_incident_dir", "vec3", "eye_incident_dir*eye_tbn_matrix", 0 }, { VERTEX, "eye_incident_dir", "vec3", "normalize(eye_vertex.xyz)", 0 }, { VERTEX, "eye_tbn_matrix", "mat3", "mat3(eye_tangent, eye_binormal, eye_normal)", 0 }, - { VERTEX, "eye_vertex", "vec4", "eye_obj_matrix*vertex", "!r" }, - { VERTEX, "eye_vertex", "vec4", "transform_vertex(vertex)", "r" }, - { VERTEX, "eye_normal", "vec3", "eye_obj_normal_matrix*normal", "!r" }, - { VERTEX, "eye_normal", "vec3", "transform_normal(normal)", "r" }, - { VERTEX, "eye_tangent", "vec3", "eye_obj_normal_matrix*tangent", "!r" }, - { VERTEX, "eye_tangent", "vec3", "transform_normal(tangent)", "r" }, - { VERTEX, "eye_binormal", "vec3", "eye_obj_normal_matrix*binormal", "!r" }, - { VERTEX, "eye_binormal", "vec3", "transform_normal(binormal)", "r" }, + { VERTEX, "eye_vertex", "vec4", "eye_obj_matrix*vertex", 0 }, + { VERTEX, "eye_normal", "vec3", "eye_obj_normal_matrix*normal", 0 }, + { VERTEX, "eye_tangent", "vec3", "eye_obj_normal_matrix*tangent", 0 }, + { VERTEX, "eye_binormal", "vec3", "eye_obj_normal_matrix*binormal", 0 }, { VERTEX, "texture_coord", "vec2", "texcoord.xy", 0 }, { ATTRIBUTE, "vertex", "vec4", "gl_Vertex", 0 }, @@ -134,7 +128,84 @@ ProgramBuilder::ProgramBuilder(const StandardFeatures &f): features(f), feature_flags(features.create_flags()), optimize(true) -{ } +{ + if(!features.custom.empty()) + { + const char *whitespace = " \t\n"; + string::size_type start = 0; + while(1) + { + start = features.custom.find_first_not_of(whitespace, start); + if(start==string::npos) + break; + + string::size_type semicolon = features.custom.find(';', start); + if(semicolon==start) + { + ++start; + continue; + } + else if(semicolon==string::npos) + throw invalid_variable_definition(features.custom.substr(start)); + + string::size_type equals = features.custom.find('=', start); + if(equals>semicolon) + equals = string::npos; + + VariableDefinition var; + string::size_type decl_end = min(equals, semicolon); + for(unsigned i=0;; ++i) + { + string::size_type word_end = features.custom.find_first_of(whitespace, start); + word_end = min(word_end, decl_end); + features.custom[word_end] = 0; + + const char *word = &features.custom[start]; + if(i==0) + { + if(!strcmp(word, "uniform")) + var.scope = UNIFORM; + else if(!strcmp(word, "attribute")) + var.scope = ATTRIBUTE; + else if(!strcmp(word, "vertex")) + var.scope = VERTEX; + else if(!strcmp(word, "fragment")) + var.scope = FRAGMENT; + else + throw invalid_variable_definition(word); + } + else if(i==1) + var.type = word; + else if(i==2) + var.name = word; + + start = features.custom.find_first_not_of(whitespace, word_end+1); + if(start>=decl_end) + break; + } + + if(equals!=string::npos) + { + start = features.custom.find_first_not_of(whitespace, equals+1); + if(start>=semicolon) + throw invalid_variable_definition("no expression"); + features.custom[semicolon] = 0; + var.expression = &features.custom[start]; + } + else + var.expression = 0; + + var.flags = 0; + for(const VariableDefinition *j=standard_variables; j->name; ++j) + if(!strcmp(var.name, j->name)) + var.flags = "o"; + + custom_variables.push_front(var); + + start = semicolon+1; + } + } +} void ProgramBuilder::set_optimize(bool o) { @@ -156,11 +227,24 @@ void ProgramBuilder::add_shaders(Program &prog) const variables.push_front(ShaderVariable("gl_Position")); variables.push_front(ShaderVariable(features.legacy ? "gl_FragColor" : "frag_color")); - for(const StandardVariable *i=standard_variables; i->name; ++i) + list::const_iterator next_custom = custom_variables.begin(); + for(const VariableDefinition *i=standard_variables; i->name; ) { - // Skip over anything that isn't used with the supplied flags - if(i->flags && !evaluate_flags(i->flags)) - continue; + const VariableDefinition *def = 0; + if(next_custom!=custom_variables.end() && (!strcmp(next_custom->name, i->name) || !next_custom->flags)) + { + def = &*next_custom; + ++next_custom; + } + else + { + def = i; + ++i; + + // Skip over anything that isn't used with the supplied flags + if(def->flags && !evaluate_flags(def->flags)) + continue; + } // See if this variable can satisfy any unresolved variables ShaderVariable *last_resolved = 0; @@ -169,7 +253,7 @@ void ProgramBuilder::add_shaders(Program &prog) const if(j->variable) continue; - if(!name_match(i->name, j->resolved_name.c_str())) + if(!name_match(def->name, j->resolved_name.c_str())) continue; if(last_resolved) @@ -181,15 +265,15 @@ void ProgramBuilder::add_shaders(Program &prog) const continue; } - j->resolve(*i); + j->resolve(*def); resolved_vars.push_front(&*j); if(!j->fuzzy_space) last_resolved = &*j; - if(!i->expression) + if(!def->expression) continue; - vector identifiers = extract_identifiers(i->expression); + vector identifiers = extract_identifiers(def->expression); for(vector::const_iterator k=identifiers.begin(); k!=identifiers.end(); ++k) { // Use an existing variable if possible, but only if it's not fuzzy @@ -277,20 +361,6 @@ string ProgramBuilder::create_source(const list &variables, Va if(scope==FRAGMENT && !features.legacy) source += "out vec4 frag_color;\n"; - if(scope==VERTEX && features.transform) - { - // Add the prototypes here, until I come up with something better - source += "vec4 transform_vertex(vec4);\n"; - source += "vec3 transform_normal(vec3);\n"; - } - else if(scope==FRAGMENT && features.colorify) - { - if(features.texture) - source += "vec4 sample_texture(vec2);\n"; - if(features.normalmap) - source += "vec3 sample_normalmap(vec2);\n"; - } - source += "void main()\n{\n"; for(list::const_iterator i=variables.begin(); i!=variables.end(); ++i) @@ -463,8 +533,6 @@ ProgramBuilder::StandardFeatures::StandardFeatures(): normalmap(false), shadow(false), reflection(false), - transform(false), - colorify(false), legacy(!(get_glsl_version()>=Version(1, 30))) { } @@ -487,10 +555,6 @@ string ProgramBuilder::StandardFeatures::create_flags() const flags += 's'; if(reflection) flags += 'e'; - if(transform) - flags += 'r'; - if(colorify) - flags += 'c'; if(legacy) flags += 'g'; @@ -507,7 +571,7 @@ ProgramBuilder::ShaderVariable::ShaderVariable(const std::string &n): inline_parens(false) { } -void ProgramBuilder::ShaderVariable::resolve(const StandardVariable &var) +void ProgramBuilder::ShaderVariable::resolve(const VariableDefinition &var) { variable = &var; const char *space = 0; @@ -624,7 +688,7 @@ string ProgramBuilder::ShaderVariable::get_expression() const ProgramBuilder::StandardFeatures::Loader::Loader(StandardFeatures &f): DataFile::ObjectLoader(f) { - add("colorify", &StandardFeatures::colorify); + add("custom", &StandardFeatures::custom); add("lighting", &StandardFeatures::lighting); add("material", &StandardFeatures::material); add("normalmap", &StandardFeatures::normalmap); @@ -632,7 +696,6 @@ ProgramBuilder::StandardFeatures::Loader::Loader(StandardFeatures &f): add("shadow", &StandardFeatures::shadow); add("specular", &StandardFeatures::specular); add("texture", &StandardFeatures::texture); - add("transform", &StandardFeatures::transform); } } // namespace GL diff --git a/source/programbuilder.h b/source/programbuilder.h index 14a90a85..7840ecf0 100644 --- a/source/programbuilder.h +++ b/source/programbuilder.h @@ -11,6 +11,13 @@ namespace GL { class Program; +class invalid_variable_definition: public std::invalid_argument +{ +public: + invalid_variable_definition(const std::string &w): std::invalid_argument(w) { } + virtual ~invalid_variable_definition() throw() { } +}; + class ProgramBuilder { public: @@ -29,9 +36,8 @@ public: bool normalmap; bool shadow; bool reflection; - bool transform; - bool colorify; bool legacy; + std::string custom; StandardFeatures(); @@ -48,7 +54,7 @@ private: FRAGMENT }; - struct StandardVariable + struct VariableDefinition { VariableScope scope; const char *name; @@ -60,7 +66,7 @@ private: struct ShaderVariable { std::string name; - const StandardVariable *variable; + const VariableDefinition *variable; std::string resolved_name; bool fuzzy_space; std::string resolved_space; @@ -71,7 +77,7 @@ private: ShaderVariable(const std::string &); - void resolve(const StandardVariable &); + void resolve(const VariableDefinition &); void resolve(ShaderVariable &); void resolve_space(const std::string &); void add_reference(ShaderVariable &); @@ -89,10 +95,11 @@ private: }; StandardFeatures features; + std::list custom_variables; std::string feature_flags; bool optimize; - static const StandardVariable standard_variables[]; + static const VariableDefinition standard_variables[]; public: ProgramBuilder(const StandardFeatures &);