391 lines
12 KiB
C++
391 lines
12 KiB
C++
#ifndef _SHADERLOADER_H_
|
|
#define _SHADERLOADER_H_
|
|
|
|
#include <fstream>
|
|
#include <libgen.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sstream>
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
|
|
#include <glad/glad.h>
|
|
#include <GLFW/glfw3.h>
|
|
|
|
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<char> 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<RegisteredShaderProgram> *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>,<type>,<filepath>
|
|
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<char> 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>,<type>,<filepath>
|
|
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<char> 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<char> 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_
|