1#include <cgv/base/base.h>
2#include "shader_program.h"
3#include <cgv/utils/file.h>
4#include <cgv/utils/dir.h>
7#include <cgv/utils/tokenizer.h>
8#include <cgv/base/import.h>
16std::map<std::string, std::vector<std::string>> shader_program::files_cache;
22 for(
unsigned int i = 0; i < file_names.size(); ++i) {
23 no_error =
attach_file(ctx, file_names[i], ST_DETECT, defines) && no_error;
32 auto it = files_cache.find(name);
33 if(it != files_cache.end()) {
34 const std::vector<std::string>& cached_file_names = it->second;
35 for(
size_t i = 0; i < cached_file_names.size(); ++i)
36 file_names.push_back(cached_file_names[i]);
38 added_files = !cached_file_names.empty();
49 bool added_files =
false;
54 std::vector<std::string> collected_file_names;
60 file_names.push_back(fn);
62 collected_file_names.push_back(fn);
67 files_cache.emplace(file_name, collected_file_names);
75 bool added_files =
false;
80 std::vector<std::string> collected_file_names;
82 const char* exts[] = {
"glvs",
"glgs",
"glfs",
"glcs",
"gltc",
"glte",
"pglvs",
"pglfs",
"pglgs",
"pglcs",
"pgltc",
"pglte", 0 };
83 const char** iter = exts;
84 bool added_file =
false;
88 file_names.push_back(fn);
91 collected_file_names.push_back(fn);
98 std::cerr <<
"could not find shader file " << base_name.c_str() << std::endl;
101 files_cache.emplace(base_name, collected_file_names);
108 std::string dn = dir_name;
109 if (!dir::exists(dn)) {
118 void* handle = file::find_first(dn+
"/*.gl*");
122 file_names.push_back(dir_name+
"/"+file::find_name(handle));
123 handle = file::find_next(handle);
139 std::vector<line> lines;
141 bool added_file =
false;
143 std::string path = file::get_path(file_name);
147 for (
auto line : lines) {
149 if (l.substr(0,5) ==
"file:")
150 added_file =
collect_file(l.substr(5), use_cache, file_names) || added_file;
151 else if (l.substr(0,12) ==
"vertex_file:")
152 added_file =
collect_file(l.substr(12), use_cache, file_names) || added_file;
153 else if (l.substr(0, 14) ==
"geometry_file:")
154 added_file =
collect_file(l.substr(14), use_cache, file_names) || added_file;
155 else if (l.substr(0, 26) ==
"tessellation_control_file:")
156 added_file =
collect_file(l.substr(26), use_cache, file_names) || added_file;
157 else if (l.substr(0, 29) ==
"tessellation_evaluation_file:")
158 added_file =
collect_file(l.substr(29), use_cache, file_names) || added_file;
159 else if (l.substr(0,14) ==
"fragment_file:")
160 added_file =
collect_file(l.substr(14), use_cache, file_names) || added_file;
161 else if (l.substr(0,6) ==
"files:")
162 added_file =
collect_files(l.substr(6), use_cache, file_names) || added_file;
163 else if (l.substr(0,4) ==
"dir:")
164 added_file =
collect_dir(l.substr(4),
false, file_names) || added_file;
165 else if (l.substr(0,8) ==
"rec_dir:")
166 added_file =
collect_dir(l.substr(8),
true, file_names) || added_file;
167 else if (l.substr(0,8) ==
"program:")
168 added_file =
collect_program(l.substr(8), use_cache, file_names) || added_file;
178 show_code_errors = _show_code_errors;
180 state_out_of_date =
true;
181 nr_attached_geometry_shaders = 0;
191 std::cerr <<
"could not destruct shader program properly" << std::endl;
197 state_out_of_date =
true;
198 nr_attached_geometry_shaders = 0;
205 return ctx.shader_program_create(*
this);
212 last_error =
"attach_code to shader program that was not created";
216 last_error =
"attempt to attach_code that is not created to shader program";
220 last_error =
"attempt to attach_code that is not compiled to shader program";
223 ctx.shader_program_attach(*
this, code);
225 ++nr_attached_geometry_shaders;
232 last_error =
"detach_code from shader program that was not created";
236 last_error =
"attempt to detach_code that is not created to shader program";
239 ctx.shader_program_detach(*
this, code);
241 --nr_attached_geometry_shaders;
250 managed_codes.push_back(code_ptr);
262 if(!code_ptr->
read_and_compile(ctx, file_name, st, show_code_errors, defines)) {
267 managed_codes.push_back(code_ptr);
273 std::vector<std::string> file_names;
280 std::vector<std::string> file_names;
304 *last_error_ptr =
"could not find shader program file " + file_name;
309 *last_error_ptr =
"could not read shader program file " + file_name;
320 std::vector<line> lines;
321 std::vector<shader_define_map> result;
324 for (
unsigned int i = 0; i < lines.size(); ++i) {
325 token tok = lines[i];
329 if (l.empty() || l[0] ==
'/')
331 if (l.substr(0, 9) !=
"instance:")
333 std::string defs=l.substr(9);
334 std::vector<token> toks;
337 for (
const auto& t : toks) {
338 std::vector<token> sides;
340 std::vector<std::string> S;
341 for (
auto& s : sides) {
342 while (s.begin < s.end &&
is_space(*s.begin))
344 while (s.begin < s.end &&
is_space(s.end[-1]))
350 defines[S[0]] = S[1];
352 result.push_back(defines);
360 std::vector<line> lines;
366 std::cout <<
"read shader program <" << file_name <<
">" << std::endl;
368 std::string path = file::get_path(file_name);
372 bool no_error =
true;
373 std::string error =
"2 : attach command failed";
374 for (
unsigned int i=0; i<lines.size(); ++i) {
375 token tok = lines[i];
387 if (l.substr(0,5) ==
"file:")
388 success =
attach_file(ctx, l.substr(5), ST_DETECT, defines);
389 else if (l.substr(0,12) ==
"vertex_file:")
390 success =
attach_file(ctx, l.substr(12), ST_VERTEX, defines);
391 else if (l.substr(0,14) ==
"geometry_file:")
392 success =
attach_file(ctx, l.substr(14), ST_GEOMETRY, defines);
393 else if (l.substr(0, 26) ==
"tessellation_control_file:")
394 success =
attach_file(ctx, l.substr(26), ST_TESS_CONTROL, defines);
395 else if (l.substr(0, 29) ==
"tessellation_evaluation_file:")
396 success =
attach_file(ctx, l.substr(29), ST_TESS_EVALUATION, defines);
397 else if (l.substr(0,14) ==
"fragment_file:")
398 success =
attach_file(ctx, l.substr(14), ST_FRAGMENT, defines);
399 else if(l.substr(0, 13) ==
"compute_file:")
400 success =
attach_file(ctx, l.substr(13), ST_COMPUTE, defines);
401 else if (l.substr(0,6) ==
"files:")
403 else if (l.substr(0,4) ==
"dir:")
404 success =
attach_dir(ctx, l.substr(4),
false);
405 else if (l.substr(0,8) ==
"rec_dir:")
407 else if (l.substr(0,8) ==
"program:")
409 else if (l.substr(0,21) ==
"geometry_shader_info:") {
410 std::vector<token> toks;
411 std::string l1 = l.substr(21);
413 if (toks.size() == 3) {
416 for (pi = PT_UNDEF+1; pi < PT_LAST; ++pi) {
424 if (i_pt == PT_UNDEF) {
425 error =
"4 : unknown input_type for geometry shader <";
429 else if (o_pt == PT_UNDEF) {
430 error =
"5 : unknown ouput_type for geometry shader <";
434 else if (!
is_integer(toks[2].begin,toks[2].end,count)) {
435 error =
"6 : max_output_count of geometry shader must be an integer but received <";
445 error =
"3 : geometry_shader_info takes three arguments separated by colons";
448 else if (l.substr(0, 9) ==
"instance:") {
450 else if (show_error) {
451 std::cerr << file_name.c_str() <<
" (" << i + 1
452 <<
"): warning G0001 : syntax error in line '"
453 << l.c_str() <<
"'" << std::endl;
457 std::cerr << file_name.c_str() <<
" (" << i+1
458 <<
"): error G000" << error.c_str() << std::endl;
465 if (show_error && !no_error)
492 if (!
link(ctx, show_error)) {
495 std::vector<line> lines;
497 std::string formated_error;
498 for (
unsigned int i = 0; i < lines.size(); ++i) {
499 formated_error += fn +
"(1) : error G0002: " +
to_string(lines[i]) +
"\n";
501 std::cerr << formated_error.c_str() << std::endl;
512 return ctx.query_integer_constant(MAX_NR_GEOMETRY_SHADER_OUTPUT_VERTICES);
518 if (state_out_of_date) {
519 if (nr_attached_geometry_shaders > 0) {
520 if (geometry_shader_output_count < 1)
522 ctx.shader_program_set_state(*
this);
524 state_out_of_date =
false;
531 if (ctx.shader_program_link(*
this)) {
538 std::cerr <<
"link error:\n" <<
last_error.c_str() << std::endl;
551 geometry_shader_input_type = input_type;
552 geometry_shader_output_type = output_type;
553 geometry_shader_output_count = max_output_count;
554 state_out_of_date =
true;
561 ctx.
error(
"attempt to enable shader_program that is not created",
this);
565 ctx.
error(
"attempt to enable shader_program that is not linked",
this);
569 ctx.
error(
"attempt to enable shader_program that is already enabled or was not disabled properly",
this);
573 bool res = ctx.shader_program_enable(*
this);
575 shader_program_base::is_enabled =
true;
583 ctx.
error(
"attempt to disable shader_program that is not enabled",
this);
586 bool res = ctx.shader_program_disable(*
this);
587 shader_program_base::is_enabled =
false;
594 return ctx.get_uniform_location(*
this, name);
600 set_uniform(ctx, name +
".brdf_type", (
int)material.get_brdf_type(), generate_error) &&
601 set_uniform(ctx, name +
".diffuse_reflectance", material.get_diffuse_reflectance(), generate_error) &&
602 set_uniform(ctx, name +
".roughness", material.get_roughness(), generate_error) &&
603 set_uniform(ctx, name +
".ambient_occlusion", material.get_ambient_occlusion(), generate_error) &&
604 set_uniform(ctx, name +
".emission", material.get_emission(), generate_error) &&
605 set_uniform(ctx, name +
".specular_reflectance", material.get_specular_reflectance(), generate_error) &&
606 set_uniform(ctx, name +
".roughness_anisotropy", material.get_roughness_anisotropy(), generate_error) &&
607 set_uniform(ctx, name +
".roughness_orientation", material.get_roughness_orientation(), generate_error) &&
608 set_uniform(ctx, name +
".propagation_slow_down",
cgv::math::fvec<float, 2>(material.get_propagation_slow_down().real(), material.get_propagation_slow_down().imag()), generate_error) &&
609 set_uniform(ctx, name +
".transparency", material.get_transparency(), generate_error) &&
610 set_uniform(ctx, name +
".metalness", material.get_metalness(), generate_error);
616 const char* texture_names[] = {
617 "tex0",
"tex1",
"tex2",
"tex3"
620 if (!
set_uniform(ctx, texture_names[i], i, generate_error))
625 set_uniform(ctx,
"diffuse_index", material.get_diffuse_index(), generate_error) &&
626 set_uniform(ctx,
"roughness_index", material.get_roughness_index(), generate_error) &&
627 set_uniform(ctx,
"metalness_index", material.get_metalness_index(), generate_error) &&
628 set_uniform(ctx,
"ambient_index", material.get_ambient_index(), generate_error) &&
629 set_uniform(ctx,
"emission_index", material.get_emission_index(), generate_error) &&
630 set_uniform(ctx,
"transparency_index", material.get_transparency_index(), generate_error) &&
631 set_uniform(ctx,
"bump_index", material.get_bump_index(), generate_error) &&
632 set_uniform(ctx,
"specular_index", material.get_specular_index(), generate_error);
640 if (!
set_uniform(ctx, name +
".light_source_type",
static_cast<int>(L.get_type()), generate_error))
642 if (!
set_uniform(ctx, name +
".position", L.get_position(), generate_error))
644 if (!
set_uniform(ctx, name +
".emission", L.get_emission(), generate_error))
646 if (!
set_uniform(ctx, name +
".ambient_scale", L.get_ambient_scale(), generate_error))
648 if (!
set_uniform(ctx, name +
".spot_direction", L.get_spot_direction(), generate_error))
650 if (!
set_uniform(ctx, name +
".spot_exponent", L.get_spot_exponent(), generate_error))
652 if (!
set_uniform(ctx, name +
".spot_cos_cutoff", cos(0.01745329252f*L.get_spot_cutoff()), generate_error))
654 if (!
set_uniform(ctx, name +
".constant_attenuation", L.get_constant_attenuation(), generate_error))
656 if (!
set_uniform(ctx, name +
".linear_attenuation", L.get_linear_attenuation(), generate_error))
658 if (!
set_uniform(ctx, name +
".quadratic_attenuation", L.get_quadratic_attenuation(), generate_error))
666 return ctx.get_attribute_location(*
this, name);
672 while (managed_codes.size() > 0) {
673 delete managed_codes.back();
674 managed_codes.pop_back();
677 ctx.shader_program_destruct(*
this);
681 state_out_of_date =
true;
682 auto_detect_uniforms =
true;
683 auto_detect_vertex_attributes =
true;
684 nr_attached_geometry_shaders = 0;
More advanced text processing for splitting text into lines or tokens.
bool empty() const
check if pointer is not yet set
A vector with zero based index.
unsigned get_nr_image_files() const
return number of image files
bool get_sRGBA_textures() const
return whether textures are interpreted in sRGB format
base class for all drawables, which is independent of the used rendering API.
virtual void error(const std::string &message, const render_component *rc=0) const
error handling
bool is_shader_file_cache_enabled() const
whether the shader file caches are enabled
virtual bool make_current() const =0
make the current context current if possible
virtual bool is_created() const
return whether component has been created
const context * ctx_ptr
keep pointer to my context
std::string last_error
a string that contains the last error
a shader code object holds a code fragment of a geometry vertex or fragment shader and can be added t...
static std::string find_file(const std::string &file_name, bool search_exhaustive=false)
Find the full path to a shader by its file name.
ShaderType get_shader_type() const
return the shader type of this code
bool is_compiled() const
return whether shader has been compiled successfully
static void decode_if_base64(std::string &content)
decode a string if it is base64 encoded
bool compile(const context &ctx)
compile attached source; returns true if successful
bool set_code(const context &ctx, const std::string &source, ShaderType st)
set shader code from string
bool read_and_compile(const context &ctx, const std::string &file_name, ShaderType st=ST_DETECT, bool show_error=true, const shader_define_map &defines=shader_define_map())
read shader code with read_code and compile.
bool enable(context &ctx)
enable the shader program
bool disable(context &ctx)
disable shader program and restore fixed functionality
static bool collect_file(const std::string &file_name, bool use_cache, std::vector< std::string > &file_names)
resolve file name with shader_code::find_file and add file to list if found
shader_program(bool _show_code_errors=false)
create empty shader program and set the option whether errors during shader code attachment should be...
static std::map< std::string, std::string > program_file_cache
maps used to cache program file contents and valid file names indexed by their respective file name
bool set_uniform(const context &ctx, const std::string &name, const T &value, bool generate_error=false)
Set the value of a uniform by name, where the type can be any of int, unsigned, float,...
static std::vector< shader_define_map > extract_instances(std::string file_name)
find and parse all instance definitions in a shader program file
static unsigned int get_max_nr_geometry_shader_output_vertices(const context &ctx)
return the maximum number of output vertices of a geometry shader
~shader_program()
call destruct method
static bool collect_files_from_cache(const std::string &name, std::vector< std::string > &file_names, bool &added_files)
resolve file name with shader_code::find_file and add file to list if found
void destruct(const context &ctx)
destruct shader program
bool build_dir(const context &ctx, const std::string &dir_name, bool recursive=false, bool show_error=false)
successively calls create, attach_dir and link.
static bool open_program_file(std::string &file_name, bool use_cache, std::string &content, std::vector< cgv::utils::line > &lines, std::string *last_error_ptr=0)
common code necessary to open file
bool detach_code(const context &ctx, const shader_code &code)
detach a shader code
bool attach_program(const context &ctx, std::string file_name, bool show_error=false, const shader_define_map &defines=shader_define_map())
collect shader code files declared in shader program file, compile and attach them
static bool collect_files(const std::string &base_name, bool use_cache, std::vector< std::string > &file_names)
collect shader code files that extent the given base name.
int get_attribute_location(const context &ctx, const std::string &name) const
query location index of an attribute
bool attach_code(const context &ctx, const shader_code &code)
attach a compiled shader code instance that is managed outside of program
void update_state(const context &ctx)
ensure that the state has been set in the context
bool create(const context &ctx)
create the shader program
void set_geometry_shader_info(PrimitiveType input_type, PrimitiveType output_type, int max_output_count=0)
configure the geometry shader, if count < 1 set it to get_max_nr_geometry_shader_output_vertices
bool is_linked() const
return whether program is linked
bool link(const context &ctx, bool show_error=false)
link shaders to an executable program
static bool collect_dir(const std::string &dir_name, bool recursive, std::vector< std::string > &file_names)
collect shader code files from directory.
bool attach_file(const context &ctx, const std::string &file_name, ShaderType st=ST_DETECT, const shader_define_map &defines=shader_define_map())
read shader code from file, compile and attach to program
bool is_enabled() const
check whether program is currently enabled
bool attach_dir(const context &ctx, const std::string &dir_name, bool recursive)
collect shader code files from directory, compile and attach.
bool set_textured_material_uniform(const context &ctx, const std::string &name, const textured_material &material, bool generate_error=false)
set a uniform of type textured_material
bool set_light_uniform(const context &ctx, const std::string &name, const cgv::media::illum::light_source &light, bool generate_error=false)
set a uniform of type light source
int get_uniform_location(const context &ctx, const std::string &name) const
query location index of an uniform
bool attach_files(const context &ctx, const std::vector< std::string > &file_names, const shader_define_map &defines=shader_define_map())
attach a list of files
bool build_files(const context &ctx, const std::string &base_name, bool show_error=false, const shader_define_map &defines=shader_define_map())
successively calls create, attach_files and link.
bool set_material_uniform(const context &ctx, const std::string &name, const cgv::media::illum::surface_material &material, bool generate_error=false)
set a uniform of type material
bool build_program(const context &ctx, const std::string &file_name, bool show_error=false, const shader_define_map &defines=shader_define_map())
successively calls create, attach_program and link.
static bool collect_program(const std::string &file_name, bool use_cache, std::vector< std::string > &file_names)
collect shader code files declared in a shader program file.
class that extends obj_material with the management of textures
the tokenizer allows to split text into tokens in a convenient way.
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
bool read_data_file(const std::string &file_name, std::string &content, bool ascii)
read ascii file into a string
std::map< std::string, std::string > shader_define_map
typedef for shader define map data structure
shader_config_ptr get_shader_config()
return a reference to the current shader configuration
ShaderType
different shader types
PrimitiveType
different primitive types
namespace that holds tools that dont fit any other namespace
void split_to_tokens(const char *begin, const char *end, std::vector< token > &tokens, const std::string &separators, bool merge_separators, const std::string &open_parenthesis, const std::string &close_parenthesis, const std::string &whitespaces, unsigned int max_nr_tokens)
this function splits a text range into tokens.
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
bool is_integer(const char *begin, const char *end, int &value)
check if the text range (begin,end( defines an integer value. If yes, store the value in the passed r...
void split_to_lines(const char *global_begin, const char *global_end, std::vector< line > &lines, bool truncate_trailing_spaces)
this function splits a text range at the newline characters into single lines.
bool is_space(char c)
check if char is a whitespace
Helper functions to process strings.
a line in a text is simply represented as a token
representation of a token in a text by two pointers begin and end, that point to the first character ...
const char * begin
pointers that define the range of characters