#ifndef SHADER_H #define SHADER_H #include enum shader_err { SHADER_ERR_OK, SHADER_ERR_FAIL_CREATE, SHADER_ERR_FAIL_CREATE_PROG, SHADER_ERR_FAIL_COMPILE, SHADER_ERR_FAIL_READ_FILE, SHADER_ERR_FAIL_LINK_PROG }; struct ret_uint32_t_err { uint32_t f1; enum shader_err f2; }; struct ret_uint32_t_err shader_compile_source(const char *src, GLenum type); struct ret_uint32_t_err shader_compile_file(const char *filepath, GLenum type); uint32_t shader_compile_file_or_exit(const char *filepath, GLenum type); struct ret_uint32_t_err program_link(uint32_t vert_shader, uint32_t frag_shader); uint32_t program_link_or_exit(uint32_t vert_shader, uint32_t frag_shader); const char *shader_type_to_cstr(GLenum type); #if defined(SHADER_IMP) || defined(IMP) #include #include "unwrap.h" #include "file.h" struct ret_uint32_t_err shader_compile_source(const char *src, GLenum type) { enum shader_err err; uint32_t shdr = glCreateShader(type); if ( shdr == 0 ) { err = SHADER_ERR_FAIL_CREATE; goto err; } glShaderSource(shdr, 1, &src, NULL); glCompileShader(shdr); int32_t comp_status = GL_FALSE; glGetShaderiv(shdr, GL_COMPILE_STATUS, &comp_status); if ( comp_status == GL_FALSE ) { err = SHADER_ERR_FAIL_COMPILE; int32_t log_length = 1024; glGetShaderiv(shdr, GL_INFO_LOG_LENGTH, &log_length); log_length += 10; char *log = calloc(sizeof(char), (size_t)log_length); glGetShaderInfoLog(shdr, log_length, NULL, log); fprintf(stderr, "FAILED COMPILE %s : %s\n", shader_type_to_cstr(type), log); goto err; } return (struct ret_uint32_t_err) { .f1 = shdr, .f2 = SHADER_ERR_OK }; err: ; return (struct ret_uint32_t_err) { .f1 = 0, .f2 = err }; } struct ret_uint32_t_err shader_compile_file(const char *filepath, GLenum type) { char *src; enum file_err err; src = (char *) file_read_all(filepath, NULL, &err); if ( err != FILE_ERR_OK ) { return (struct ret_uint32_t_err) { .f1 = 0, .f2 = SHADER_ERR_FAIL_READ_FILE, }; } struct ret_uint32_t_err ret = shader_compile_source(src, type); free(src); return ret; } uint32_t shader_compile_file_or_exit(const char *filepath, GLenum type) { enum shader_err err; uint32_t shader; RET_UNWRAP2(shader, err, struct ret_uint32_t_err, shader_compile_file(filepath, type)); if ( err != SHADER_ERR_OK ) { fprintf(stderr, "Failed to compile shader %s\n", filepath); exit(EXIT_FAILURE); } return shader; } struct ret_uint32_t_err program_link(uint32_t vert_shader, uint32_t frag_shader) { enum shader_err err; uint32_t prog = glCreateProgram(); if ( prog == 0 ) { err = SHADER_ERR_FAIL_CREATE_PROG; goto err; } glAttachShader(prog, vert_shader); glAttachShader(prog, frag_shader); glLinkProgram(prog); int32_t link_status = GL_FALSE; glGetProgramiv(prog, GL_LINK_STATUS, &link_status); if ( link_status != GL_TRUE ) { err = SHADER_ERR_FAIL_LINK_PROG; int32_t log_length = 1024; glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_length); log_length += 10; char *log = calloc(sizeof(char), (size_t)log_length); glGetProgramInfoLog(prog, log_length, NULL, log); fprintf(stderr, "FAILED LINK PROG: %s\n", log); goto err; } glDeleteShader(vert_shader); glDeleteShader(frag_shader); return (struct ret_uint32_t_err) { .f1 = prog, .f2 = SHADER_ERR_OK, }; err: ; return (struct ret_uint32_t_err) { .f1 = 0, .f2 = err, }; } uint32_t program_link_or_exit(uint32_t vert_shader, uint32_t frag_shader) { enum shader_err err; uint32_t prog; RET_UNWRAP2(prog, err, struct ret_uint32_t_err, program_link(vert_shader, frag_shader)); if ( err != SHADER_ERR_OK ) { fprintf(stderr, "Failed to link program\n"); exit(EXIT_FAILURE); } return prog; } const char * shader_type_to_cstr(GLenum type) { switch ( type ) { case GL_VERTEX_SHADER: return "GL_VERTEX_SHADER"; break; case GL_FRAGMENT_SHADER: return "GL_FRAGMENT_SHADER"; break; } return "(Unknown Shader)"; } #endif /* defined(SHADER_IMP) || defined(IMP) */ #endif