#ifndef _SHADERLOADER_H_ #define _SHADERLOADER_H_ #include #include #include #include #include #include #include #include #include enum ShaderType { invalid = 0, vertex = GL_VERTEX_SHADER, fragment = GL_FRAGMENT_SHADER, geometry = GL_GEOMETRY_SHADER, //tesselation = GL_TESS_CONTROL_SHADER, // GL4.0 || ARB_tesselation_shader //valuation = GL_TESS_EVALUATION_SHADER, //compute = GL_COMPUTE_SHADER, // GL4.3 || ARB_compute_shader }; struct RegisteredShader { char *shaderName; int shaderType; GLuint shaderID; } RegisteredShader; class RegisteredShaderProgram { char *programName; GLuint programID; public: RegisteredShaderProgram(char *name, GLuint programID) { int name_size = strlen(name); char *n = (char*) calloc(name_size, sizeof(char)); strncpy(n, name, name_size); this->programName = n; this->programID = programID; }; ~RegisteredShaderProgram() { free(this->programName); } }; struct ShaderRegistry { struct RegisteredShader *list = 0; int size = 0; } ShaderRegistry; void register_shader(struct ShaderRegistry *r, char *name, int type, GLuint id) { // Allocate storage for the name int name_size = strlen(name); char *n = (char*) calloc(name_size, sizeof(char)); strncpy(n, name, strlen(name)); // Grow and copy int new_size = r->size + 1; struct RegisteredShader *list = (struct RegisteredShader *) malloc(new_size * sizeof(RegisteredShader)); memcpy(list, r->list, r->size * sizeof(RegisteredShader)); // Add new element list[new_size-1].shaderName = n; list[new_size-1].shaderType = type; list[new_size-1].shaderID = id; // Replace in registry r->size = new_size; free(r->list); r->list = list; } struct RegisteredShader* find_shader(struct ShaderRegistry *r, char *name) { int i = 0; struct RegisteredShader *s = 0; for(i; i < r->size; i++) { s = &r->list[i]; if(strcmp(s->shaderName, name) == 0) { return s; } } return 0; } void remove_shader(struct ShaderRegistry *r, struct RegisteredShader *s) { if(s == 0) { return; } int index = (s - (r->list)) / sizeof(struct RegisteredShader); int new_size = r->size - 1; struct RegisteredShader *list; list = (struct RegisteredShader*) malloc(new_size * sizeof(struct RegisteredShader)); // Copy the first half memcpy(list, r->list, index * sizeof(struct RegisteredShader)); // Copy the second half memcpy(list, r->list + ((index + 1) * sizeof(struct RegisteredShader)), (r->size - 1 - index) * sizeof(struct RegisteredShader)); // Deallocate the string of the registered shader free(s->shaderName); // Deallocate the old list free(r->list); r->list = list; r->size = new_size; } void remove_shader_by_name(struct ShaderRegistry *r, char *name) { remove_shader(r, find_shader(r, name)); } class ShaderProgramRegistry { struct RegisteredShareProgram *list = 0; int size = 0; }; GLuint compile_shader(const char* file_path, int t) { GLuint shader_id = glCreateShader(t); // From: https://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ // Read the Vertex Shader code from the file std::string code; std::ifstream stream(file_path, std::ios::in); if(stream.is_open()){ std::stringstream sstr; sstr << stream.rdbuf(); code = sstr.str(); stream.close(); } else { // @TODO Inform user. return 0; } GLint Result = GL_FALSE; int InfoLogLength; // Compile Vertex Shader printf("Compiling shader : %s\n", file_path); char const *source = code.c_str(); glShaderSource(shader_id, 1, &source , NULL); glCompileShader(shader_id); glGetShaderiv(shader_id, GL_COMPILE_STATUS, &Result); glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &InfoLogLength); if ( InfoLogLength > 0 ){ std::vector error_message(InfoLogLength+1); glGetShaderInfoLog(shader_id, InfoLogLength, NULL, &error_message[0]); printf("%s\n", &error_message[0]); } else { printf("compiled shader %d from source %s\n", shader_id, file_path); } return shader_id; } int loadShaderProgramsFromFile(const char* file_path, struct ShaderRegistry *shader_registry, std::vector *program_registry) { FILE *fp; char *line; size_t size = 0; const char *delim = ":"; const char *delim2 = ","; fp = fopen(file_path, "r"); if (fp == NULL) { return -1; } char *name; char *program_list; char *file_path_copy = (char*) calloc(strlen(file_path), sizeof(char)); strncpy(file_path_copy, file_path, strlen(file_path)); char *base = dirname((char*) file_path_copy); while((size = getline(&line, &size, fp)) != -1) { if(size == 0) { // Skipping, line length 0 continue; } if(line[0] == '#') { // Skipping, line is a comment continue; } char *line_copy = (char*) calloc(size, sizeof(char*)); strncpy(line_copy, line, size); printf("%s\n", line_copy); // Format ,, name = strtok(line_copy, delim); program_list = strtok(NULL, delim); printf("%s\n", program_list); if (name == NULL || program_list == NULL) { // Invalid line, not enough elements. // @TODO Inform the user. free(line_copy); name = program_list = NULL; continue; } // Start a new Shader Program char *program_list_copy = (char*) calloc(strlen(program_list), sizeof(char)); strncpy(program_list_copy, program_list, strlen(program_list)); char *new_line_pos = strrchr(program_list_copy, '\n'); if (new_line_pos) { new_line_pos[0] = 0; } printf("%s\n", program_list_copy); char *shader_name = strtok(program_list_copy, delim2); char *last_shader_name = NULL; GLuint program = glCreateProgram(); GLint Result = GL_FALSE; do { if (last_shader_name != NULL && shader_name == last_shader_name) { shader_name = NULL; break; } printf("sname %s ; last_shader %s\n", shader_name, last_shader_name); struct RegisteredShader *rs = find_shader(shader_registry, shader_name); if (rs == NULL) { // Should this be an error, or a warning? // @TODO Inform the user printf("could not find shader '%s'", shader_name); continue; } printf("linking program %d with shader %d\n", program, rs->shaderID); glAttachShader(program, rs->shaderID); last_shader_name = shader_name; shader_name = strtok(NULL, delim2); } while (shader_name != NULL); glLinkProgram(program); // Check the program int InfoLogLength; glGetProgramiv(program, GL_LINK_STATUS, &Result); glGetProgramiv(program, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0){ std::vector ProgramErrorMessage(InfoLogLength+1); glGetProgramInfoLog(program, InfoLogLength, NULL, &ProgramErrorMessage[0]); printf("%s\n", &ProgramErrorMessage[0]); } // Add to program_registry if (InfoLogLength == 0) { program_registry->push_back(RegisteredShaderProgram(name, program)); } free(program_list_copy); free(line_copy); } return 0; } int loadShadersFromFile(const char* file_path, struct ShaderRegistry *registry) { FILE *fp; char *line; size_t size = 0; const char *delim = ","; fp = fopen(file_path, "r"); if (fp == NULL) { return -1; } char *name; char *type; char *path; char *file_path_copy = (char*) calloc(strlen(file_path), sizeof(char)); strncpy(file_path_copy, file_path, strlen(file_path)); char *base = dirname((char*) file_path_copy); while((size = getline(&line, &size, fp)) != -1) { if(size == 0) { // Skipping, line length 0 continue; } if(line[0] == '#') { // Skipping, line is a comment continue; } char *line_copy = (char*) malloc((size) * sizeof(char*)); strncpy(line_copy, line, size); // Format ,, name = strtok(line_copy, delim); type = strtok(NULL, delim); path = strtok(NULL, delim); if (name == NULL || type == NULL || path == NULL) { // Invalid line, not enough elements. // @TODO Inform the user. free(line_copy); name = type = path = NULL; continue; } // Strip trailing new lines from the path. path[strcspn(path, "\n")] = 0; // Convert any paths as relative to the basename of the file_path they // were loaded from. int real_path_size = strlen(base) + 1 + strlen(path) + 1; char *shader_path = (char*) calloc(real_path_size, sizeof(char)); strncat(shader_path, base, strlen(base)); strcat(shader_path + strlen(base), "/"); strncat(shader_path + strlen(base) + 1, path, strlen(path)); // Validate type enum ShaderType shader_type; if(strcmp("fragment", type) == 0) { shader_type = fragment; } else if(strcmp("vertex", type) == 0) { shader_type = vertex; } else if(strcmp("geometry", type) == 0) { shader_type = geometry; } else { shader_type = invalid; } if(shader_type == invalid) { // Invalid line, unknown type. // @TODO Inform the user. free(line_copy); free(shader_path); name = type = path = NULL; continue; } GLuint shader_id = compile_shader(shader_path, shader_type); if(shader_id == 0) { // Error in compilation of the shader // @TODO Inform the user. free(line_copy); free(shader_path); name = type = path = NULL; continue; } // Add to registry register_shader(registry, name, shader_type, shader_id); // Cleanup free(line_copy); free(shader_path); } return 0; } // GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){ // // Check Vertex Shader // glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); // glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); // if ( InfoLogLength > 0 ){ // std::vector VertexShaderErrorMessage(InfoLogLength+1); // glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); // printf("%s\n", &VertexShaderErrorMessage[0]); // } // // Compile Fragment Shader // printf("Compiling shader : %s\n", fragment_file_path); // char const * FragmentSourcePointer = FragmentShaderCode.c_str(); // glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); // glCompileShader(FragmentShaderID); // // Check Fragment Shader // // Link the program // printf("Linking program\n"); // GLuint ProgramID = glCreateProgram(); // glAttachShader(ProgramID, VertexShaderID); // glAttachShader(ProgramID, FragmentShaderID); // glLinkProgram(ProgramID); // // Check the program // glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); // glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); // if ( InfoLogLength > 0 ){ // std::vector ProgramErrorMessage(InfoLogLength+1); // glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); // printf("%s\n", &ProgramErrorMessage[0]); // } // glDetachShader(ProgramID, VertexShaderID); // glDetachShader(ProgramID, FragmentShaderID); // glDeleteShader(VertexShaderID); // glDeleteShader(FragmentShaderID); // return ProgramID; // } #endif // _SHADERLOADER_H_