1#include <cgv/base/base.h>
2#include "shader_code.h"
3#include <cgv/base/register.h>
4#include <cgv/base/import.h>
6#include <cgv/utils/tokenizer.h>
7#include <cgv/ppp/ph_processor.h>
8#include <cgv/utils/dir.h>
9#include <cgv/utils/file.h>
10#include <cgv/type/variant.h>
13#pragma warning(disable:4996)
37 return "shader_config";
54 if (
getenv(
"CGV_SHADER_PATH"))
56 else if (
getenv(
"CGV_DIR"))
58 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_g2d/glsl;" +
59 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_gl/glsl;"+
60 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_gpgpu/glsl;" +
61 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_overlay/glsl;" +
62 std::string(
getenv(
"CGV_DIR")) +
"/libs/holo_disp;" +
63 std::string(
getenv(
"CGV_DIR")) +
"/libs/plot/glsl;"+
64 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_proc/glsl;"+
65 std::string(
getenv(
"CGV_DIR")) +
"/libs/rgbd_render;"+
66 std::string(
getenv(
"CGV_DIR")) +
"/plugins/examples";
73 if (!content.empty()) {
75 if(content[0] ==
char(0xA7))
98 std::vector<line> lines;
101 for (
unsigned int i = 0;
i<lines.size(); ++
i) {
103 if (
to_upper(
l.substr(0, 5)) ==
"ERROR") {
104 std::vector<token>
toks;
107 if (
toks.size() > 4) {
120 std::vector<token>
toks;
122 unsigned int i,
j, k;
int n;
123 for (
i=0;
i<
toks.size(); ++
i) {
127 for (
j=0;
j<
toks.size(); ++
j) {
131 for (k=0; k<
toks.size(); ++k) {
136 std::string
_fn =
fn;
170 std::cerr <<
"shader code not destructed correctly" << std::endl;
178 ctx.shader_code_destruct(*
this);
184 if (file_name.substr(0, 6) ==
"str://" || file_name.substr(0, 6) ==
"res://") {
185 std::map<std::string, resource_file_info>::const_iterator
it =
ref_resource_file_map().find(file_name.substr(6));
191 if (file::exists(file_name))
198 std::map<std::string, resource_file_info>::const_iterator
it =
201 if (
it->second.file_offset == -1)
202 return std::string(
"str://") + file_name;
204 return std::string(
"res://") + file_name;
207 try_name = std::string(
"glsl/") + file_name;
223 if(
end_pos == std::string::npos) {
235 std::string
ext = file::get_extension(file_name);
236 if(
ext.length() > 2) {
237 if(
ext[0] ==
'g' &&
ext[1] ==
'l' ||
238 ext[0] ==
'p' &&
ext[1] ==
'g' &&
ext[2] ==
'l')
261 std::string source =
"";
283 std::string
ext =
to_lower(file::get_extension(file_name));
285 if (
ext ==
"glfs" ||
ext ==
"pglfs")
287 else if (
ext ==
"glgs" ||
ext ==
"pglgs")
289 else if (
ext ==
"glcs" ||
ext ==
"pglcs")
291 else if (
ext ==
"gltc" ||
ext ==
"pgltc")
292 st = ST_TESS_CONTROL;
293 else if (
ext ==
"glte" ||
ext ==
"pglte")
294 st = ST_TESS_EVALUATION;
304 std::vector<line> lines;
307 for(
size_t i = 0;
i < lines.size(); ++
i) {
344std::string shader_code::resolve_includes(
const std::string& source,
bool use_cache, std::string*
_last_error) {
349void shader_code::resolve_version_and_extensions(std::string& source) {
358 size_t length()
const {
377 static const std::map<std::string, preprocessor_directive_t::Type>
identifier_to_type = {
378 {
"#version", preprocessor_directive_t::Type::kVersion },
379 {
"#extension", preprocessor_directive_t::Type::kExtension },
389 std::vector<preprocessor_directive_t>
directives;
395 while(
i < source.length()) {
400 if(
comment == CommentType::kNone ||
comment == CommentType::kSpecial) {
404 end = source.find(
'\n', end + 1);
405 }
while(end != std::string::npos && source[end - 1] ==
'\\');
407 if(end == std::string::npos)
408 end = source.length();
415 size_t space = source.find(
' ',
i + 1);
434 if(
comment == CommentType::kNone && (
i + 1) < source.length()) {
435 if(source[
i + 1] ==
'/')
437 else if(source[
i + 1] ==
'*')
443 if(
comment == CommentType::kBlock && (
i + 1) < source.length()) {
444 if(source[
i + 1] ==
'/')
451 if(
comment == CommentType::kLine)
452 comment = CommentType::kSpecial;
455 if(
comment != CommentType::kBlock)
474 if(
directive.type != preprocessor_directive_t::Type::kVersion)
479 std::vector<cgv::utils::token> tokens;
484 if(!tokens.empty()) {
489 if(tokens.size() > 1) {
492 version.has_profile =
true;
493 version.is_core_profile =
true;
494 }
else if(
profile ==
"compatibility") {
495 version.has_profile =
true;
496 version.is_core_profile =
false;
503 if(version.has_profile) {
505 if(version.is_core_profile)
516 if(!
directive.is_special_comment && (
directive.type == preprocessor_directive_t::Type::kVersion ||
directive.type == preprocessor_directive_t::Type::kExtension)) {
534 if(
directive.type == preprocessor_directive_t::Type::kExtension)
568 std::cout <<
"read shader code <" <<
fn <<
">" << std::endl;
572 if (file::get_extension(file_name)[0] ==
'p') {
575 std::string
paths = file::get_path(
fn);
581 if (!
php.parse_string(source))
583 if (!
php.process_to_string(
code))
585 cgv::ppp::clear_variables();
602 resolve_version_and_extensions(source);
604 set_defines_and_snippets(source, options);
607 set_vertex_attrib_locations(source);
621 return ctx.shader_code_create(*
this,
st, source);
642 std::map<std::string, directive_t*>
directives;
646 std::vector<cgv::utils::line> lines;
648 for(
size_t i = 0;
i < lines.size(); ++
i) {
658 for(; ptr <
line.end; ++ptr) {
664 }
else if(c ==
'/') {
665 if(
line.end - ptr > 7) {
678 std::vector<cgv::utils::token> tokens;
684 case DirectiveType::kDefine:
686 if(tokens.size() > 1) {
696 if(tokens.size() > 2) {
703 case DirectiveType::kSnippet:
705 if(tokens.size() == 1) {
706 tokens.front().begin += 3;
707 if(!tokens.front().empty())
724 for(
const auto&
define : options.get_macros()) {
727 it->second->replacement_list =
define.second;
733 const shader_compile_options::string_map& snippets = options.
get_snippets();
734 if(!snippets.empty())
739 out.reserve(source.length());
741 for(
size_t i = 0;
i < lines.size(); ++
i) {
749 case DirectiveType::kDefine:
752 case DirectiveType::kSnippet:
753 if(!snippets.empty()) {
754 auto it = std::find_if(snippets.begin(), snippets.end(), [
directive](
const std::pair<std::string, std::string>&
snippet) {
755 return directive->identifier ==
"cgv::" + snippet.first;
757 if(
it != snippets.
end())
786 source = std::move(
out);
790void shader_code::set_vertex_attrib_locations(std::string& source)
792 struct vertex_attribute {
795 std::string type =
"";
796 std::string name =
"";
801 std::string
str =
"layout (location = ";
802 str += std::to_string(location);
810 std::vector<token>
parts;
811 std::vector<token> tokens;
812 std::vector<vertex_attribute>
attribs;
831 if(tokens.size() > 1 && tokens[0] ==
"#version") {
840 if(tokens.size() == 3 && tokens[2] ==
"core")
850 if(tokens.size() > 1 && tokens[0] ==
"#define") {
851 if(tokens[1] ==
"NO_UPGRADE") {
873 for(
unsigned i = 0;
i <
tok.size() - 2; ++
i) {
884 if(c ==
'i' &&
tok[
i + 1] ==
'n' &&
tok[
i + 2] ==
' ') {
908 size_t size = tokens.size();
910 if(tokens.size() > 2) {
917 for(
size_t j = 0;
j < size; ++
j) {
918 auto&
tok = tokens[
j];
919 if(
tok.size() == 1 &&
tok[0] ==
'=')
947 for(
size_t i = 0;
i <
parts.size(); ++
i) {
957 std::string
str =
"";
971 std::string
first_part = source.substr(0, offset);
972 std::string
second_part = source.substr(offset + length + 1);
982 if (!ctx.shader_code_compile(*
this)) {
987 user_data = (
void*&)
id;
1009 return user_data != 0;
More advanced text processing for splitting text into lines or tokens.
virtual bool end()
perform the leave part of the action on the current object
complete implementation of method actions that only call one method when entering a node
bool begin()
uses call_method of base class method_action to call the method refered to by the stored method point...
reference counted pointer, which can work together with types that are derived from ref_counted,...
bool empty() const
check if pointer is not yet set
the pre header processor parses a pre header file and converts it to a header file or uses the inform...
the self reflection handler is passed to the virtual self_reflect() method of cgv::base::base.
bool reflect_member(const std::string &member_name, T &member_ref, bool hard_cast=false)
call this to reflect a member by member name and reference to the member.
base class for all drawables, which is independent of the used rendering API.
virtual GPUVendorID get_gpu_vendor_id() const
device information
bool is_shader_file_cache_enabled() const
whether the shader file caches are enabled
const context * ctx_ptr
keep pointer to my context
std::string last_error
a string that contains the last error
static bool shader_file_name_map_initialized
whether the shader file name map is initialized
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.
shader_code()
create shader a shader code object
static std::string read_code_file(const std::string &file_name, std::string *_last_error=0)
read shader code from file and return string with content or empty string if read failed
static std::map< std::string, std::string > code_cache
map that caches shader file contents indexed by their 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
void destruct(const context &ctx)
destruct shader code
~shader_code()
calls the destruct method
static void decode_if_base64(std::string &content)
decode a string if it is base64 encoded
static std::string retrieve_code(const std::string &file_name, bool use_cache, std::string *_last_error)
retreive shader code either by reading the file from disk or from the cache if enabled
ShaderType st
store the shader type
static std::string get_last_error(const std::string &file_name, const std::string &last_error)
format given last error in a way that developer environments can locate errors in the source file
static std::map< std::string, std::string > shader_file_name_map
map that caches full shader file paths indexed by the shader file name
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_code(const context &ctx, const std::string &file_name, ShaderType st=ST_DETECT, const shader_compile_options &options={})
read shader code from file that is searched for with find_file.
bool read_and_compile(const context &ctx, const std::string &file_name, ShaderType st=ST_DETECT, const shader_compile_options &options={}, bool show_error=true)
read shader code with read_code and compile.
static ShaderType detect_shader_type(const std::string &file_name)
detect the shader type from the extension of the given file_name, i.e.
Stores preprocessor options used for conditionally compiling shader programs.
bool empty() const
Return true if no options are set.
const string_map & get_snippets() const
Return const reference to defined snippets.
the tokenizer allows to split text into tokens in a convenient way.
tokenizer & set_sep(const std::string &sep, bool merge)
set the list of separators and specify whether succeeding separators are merged into single tokens
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
the base namespace holds the base hierarchy, support for plugin registration and signals
bool read_data_file(const std::string &file_name, std::string &content, bool ascii)
read ascii file into a string
std::map< std::string, resource_file_info > & ref_resource_file_map()
return a reference to a mapping of resource file names to resource file infos
void register_object(base_ptr object, const std::string &options)
register an object and send event to all current registration ref_listeners()
std::string & ref_prog_name()
return a refence to the name of the started executable
shader_config_ptr get_shader_config()
return a reference to the current shader configuration
cgv::data::ref_ptr< shader_config > shader_config_ptr
type of ref counted pointer to shader configuration
ShaderType
different shader types
namespace for compile time type information
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...
std::string & trim(std::string &str, const std::string &chars)
trim white space or other characters from start and end of string
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.
char to_lower(char c)
convert char to lower case
char to_upper(char c)
convert char to upper case
bool is_space(char c)
check if char is a whitespace
std::string decode_base64(std::string const &encoded_string)
decode a base64 encoded string
std::string strip_cpp_comments(const std::string &source, bool correct_new_lines)
remove cpp-style comments from string
bool from_string(std::string &v, const std::string &s)
specialization to extract string value from string
this header is dependency free
a globally unique shader config is registered by default when the cgv library is used.
std::string get_type_name() const
return "shader_config"
bool show_file_paths
whether to output full paths of read shaders
shader_config()
construct config without file name tracing
bool trace_file_names
whether to keep track of file names
bool self_reflect(cgv::reflect::reflection_handler &srh)
reflect the shader_path member
std::string shader_path
the path used to find shaders with the cgv::utils::file::find_in_paths function
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 ...
bool empty() const
return whether the token is empty
const char * begin
pointers that define the range of characters