nuclear@12: /* nuclear@12: glviewvol is an OpenGL 3D volume data viewer nuclear@12: Copyright (C) 2014 John Tsiombikas nuclear@12: nuclear@12: This program is free software: you can redistribute it and/or modify nuclear@12: it under the terms of the GNU General Public License as published by nuclear@12: the Free Software Foundation, either version 3 of the License, or nuclear@12: (at your option) any later version. nuclear@12: nuclear@12: This program is distributed in the hope that it will be useful, nuclear@12: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@12: GNU General Public License for more details. nuclear@12: nuclear@12: You should have received a copy of the GNU General Public License nuclear@12: along with this program. If not, see . nuclear@12: */ nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: nuclear@4: #if defined(unix) || defined(__unix__) nuclear@4: #include nuclear@4: #include nuclear@4: #endif /* unix */ nuclear@4: nuclear@4: #include "sdr.h" nuclear@4: nuclear@4: static const char *sdrtypestr(unsigned int sdrtype); nuclear@4: nuclear@4: unsigned int create_vertex_shader(const char *src) nuclear@4: { nuclear@4: return create_shader(src, GL_VERTEX_SHADER); nuclear@4: } nuclear@4: nuclear@4: unsigned int create_pixel_shader(const char *src) nuclear@4: { nuclear@4: return create_shader(src, GL_FRAGMENT_SHADER); nuclear@4: } nuclear@4: nuclear@4: unsigned int create_tessctl_shader(const char *src) nuclear@4: { nuclear@4: #ifdef GL_TESS_CONTROL_SHADER nuclear@4: return create_shader(src, GL_TESS_CONTROL_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int create_tesseval_shader(const char *src) nuclear@4: { nuclear@4: #ifdef GL_TESS_EVALUATION_SHADER nuclear@4: return create_shader(src, GL_TESS_EVALUATION_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int create_geometry_shader(const char *src) nuclear@4: { nuclear@4: #ifdef GL_GEOMETRY_SHADER nuclear@4: return create_shader(src, GL_GEOMETRY_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int create_shader(const char *src, unsigned int sdr_type) nuclear@4: { nuclear@4: unsigned int sdr; nuclear@4: int success, info_len; nuclear@4: char *info_str = 0; nuclear@4: GLenum err; nuclear@4: nuclear@4: sdr = glCreateShader(sdr_type); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: glShaderSource(sdr, 1, &src, 0); nuclear@4: err = glGetError(); nuclear@4: assert(err == GL_NO_ERROR); nuclear@4: glCompileShader(sdr); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: nuclear@4: glGetShaderiv(sdr, GL_COMPILE_STATUS, &success); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: nuclear@4: if(info_len) { nuclear@4: if((info_str = malloc(info_len + 1))) { nuclear@4: glGetShaderInfoLog(sdr, info_len, 0, info_str); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: info_str[info_len] = 0; nuclear@4: } nuclear@4: } nuclear@4: nuclear@4: if(success) { nuclear@4: fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str); nuclear@4: } else { nuclear@4: fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str); nuclear@4: glDeleteShader(sdr); nuclear@4: sdr = 0; nuclear@4: } nuclear@4: nuclear@4: free(info_str); nuclear@4: return sdr; nuclear@4: } nuclear@4: nuclear@4: void free_shader(unsigned int sdr) nuclear@4: { nuclear@4: glDeleteShader(sdr); nuclear@4: } nuclear@4: nuclear@4: unsigned int load_vertex_shader(const char *fname) nuclear@4: { nuclear@4: return load_shader(fname, GL_VERTEX_SHADER); nuclear@4: } nuclear@4: nuclear@4: unsigned int load_pixel_shader(const char *fname) nuclear@4: { nuclear@4: return load_shader(fname, GL_FRAGMENT_SHADER); nuclear@4: } nuclear@4: nuclear@4: unsigned int load_tessctl_shader(const char *fname) nuclear@4: { nuclear@4: #ifdef GL_TESS_CONTROL_SHADER nuclear@4: return load_shader(fname, GL_TESS_CONTROL_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int load_tesseval_shader(const char *fname) nuclear@4: { nuclear@4: #ifdef GL_TESS_EVALUATION_SHADER nuclear@4: return load_shader(fname, GL_TESS_EVALUATION_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int load_geometry_shader(const char *fname) nuclear@4: { nuclear@4: #ifdef GL_GEOMETRY_SHADER nuclear@4: return load_shader(fname, GL_GEOMETRY_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int load_shader(const char *fname, unsigned int sdr_type) nuclear@4: { nuclear@4: #if defined(unix) || defined(__unix__) nuclear@4: struct stat st; nuclear@4: #endif nuclear@4: unsigned int sdr; nuclear@4: size_t filesize; nuclear@4: FILE *fp; nuclear@4: char *src; nuclear@4: nuclear@4: if(!(fp = fopen(fname, "rb"))) { nuclear@4: fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno)); nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: #if defined(unix) || defined(__unix__) nuclear@4: fstat(fileno(fp), &st); nuclear@4: filesize = st.st_size; nuclear@4: #else nuclear@4: fseek(fp, 0, SEEK_END); nuclear@4: filesize = ftell(fp); nuclear@4: fseek(fp, 0, SEEK_SET); nuclear@4: #endif /* unix */ nuclear@4: nuclear@4: if(!(src = malloc(filesize + 1))) { nuclear@4: fclose(fp); nuclear@4: return 0; nuclear@4: } nuclear@4: fread(src, 1, filesize, fp); nuclear@4: src[filesize] = 0; nuclear@4: fclose(fp); nuclear@4: nuclear@4: fprintf(stderr, "compiling %s shader: %s... ", sdrtypestr(sdr_type), fname); nuclear@4: sdr = create_shader(src, sdr_type); nuclear@4: nuclear@4: free(src); nuclear@4: return sdr; nuclear@4: } nuclear@4: nuclear@4: nuclear@4: unsigned int get_vertex_shader(const char *fname) nuclear@4: { nuclear@4: return get_shader(fname, GL_VERTEX_SHADER); nuclear@4: } nuclear@4: nuclear@4: unsigned int get_pixel_shader(const char *fname) nuclear@4: { nuclear@4: return get_shader(fname, GL_FRAGMENT_SHADER); nuclear@4: } nuclear@4: nuclear@4: unsigned int get_tessctl_shader(const char *fname) nuclear@4: { nuclear@4: #ifdef GL_TESS_CONTROL_SHADER nuclear@4: return get_shader(fname, GL_TESS_CONTROL_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int get_tesseval_shader(const char *fname) nuclear@4: { nuclear@4: #ifdef GL_TESS_EVALUATION_SHADER nuclear@4: return get_shader(fname, GL_TESS_EVALUATION_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int get_geometry_shader(const char *fname) nuclear@4: { nuclear@4: #ifdef GL_GEOMETRY_SHADER nuclear@4: return get_shader(fname, GL_GEOMETRY_SHADER); nuclear@4: #else nuclear@4: return 0; nuclear@4: #endif nuclear@4: } nuclear@4: nuclear@4: unsigned int get_shader(const char *fname, unsigned int sdr_type) nuclear@4: { nuclear@4: unsigned int sdr; nuclear@4: if(!(sdr = load_shader(fname, sdr_type))) { nuclear@4: return 0; nuclear@4: } nuclear@4: return sdr; nuclear@4: } nuclear@4: nuclear@4: nuclear@4: /* ---- gpu programs ---- */ nuclear@4: nuclear@4: unsigned int create_program(void) nuclear@4: { nuclear@4: unsigned int prog = glCreateProgram(); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: return prog; nuclear@4: } nuclear@4: nuclear@4: unsigned int create_program_link(unsigned int sdr0, ...) nuclear@4: { nuclear@4: unsigned int prog, sdr; nuclear@4: va_list ap; nuclear@4: nuclear@4: if(!(prog = create_program())) { nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: attach_shader(prog, sdr0); nuclear@4: if(glGetError()) { nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: va_start(ap, sdr0); nuclear@4: while((sdr = va_arg(ap, unsigned int))) { nuclear@4: attach_shader(prog, sdr); nuclear@4: if(glGetError()) { nuclear@4: return 0; nuclear@4: } nuclear@4: } nuclear@4: va_end(ap); nuclear@4: nuclear@4: if(link_program(prog) == -1) { nuclear@4: free_program(prog); nuclear@4: return 0; nuclear@4: } nuclear@4: return prog; nuclear@4: } nuclear@4: nuclear@4: unsigned int create_program_load(const char *vfile, const char *pfile) nuclear@4: { nuclear@4: unsigned int vs = 0, ps = 0; nuclear@4: nuclear@4: if(vfile && *vfile && !(vs = get_vertex_shader(vfile))) { nuclear@4: return 0; nuclear@4: } nuclear@4: if(pfile && *pfile && !(ps = get_pixel_shader(pfile))) { nuclear@4: return 0; nuclear@4: } nuclear@4: return create_program_link(vs, ps, 0); nuclear@4: } nuclear@4: nuclear@4: void free_program(unsigned int sdr) nuclear@4: { nuclear@4: glDeleteProgram(sdr); nuclear@4: } nuclear@4: nuclear@4: void attach_shader(unsigned int prog, unsigned int sdr) nuclear@4: { nuclear@4: if(prog && sdr) { nuclear@4: glAttachShader(prog, sdr); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: } nuclear@4: } nuclear@4: nuclear@4: int link_program(unsigned int prog) nuclear@4: { nuclear@4: int linked, info_len, retval = 0; nuclear@4: char *info_str = 0; nuclear@4: nuclear@4: glLinkProgram(prog); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: glGetProgramiv(prog, GL_LINK_STATUS, &linked); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: nuclear@4: if(info_len) { nuclear@4: if((info_str = malloc(info_len + 1))) { nuclear@4: glGetProgramInfoLog(prog, info_len, 0, info_str); nuclear@4: assert(glGetError() == GL_NO_ERROR); nuclear@4: info_str[info_len] = 0; nuclear@4: } nuclear@4: } nuclear@4: nuclear@4: if(linked) { nuclear@4: fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str); nuclear@4: } else { nuclear@4: fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str); nuclear@4: retval = -1; nuclear@4: } nuclear@4: nuclear@4: free(info_str); nuclear@4: return retval; nuclear@4: } nuclear@4: nuclear@4: int bind_program(unsigned int prog) nuclear@4: { nuclear@4: GLenum err; nuclear@4: nuclear@4: glUseProgram(prog); nuclear@4: if(prog && (err = glGetError()) != GL_NO_ERROR) { nuclear@4: /* maybe the program is not linked, try linking first */ nuclear@4: if(err == GL_INVALID_OPERATION) { nuclear@4: if(link_program(prog) == -1) { nuclear@4: return -1; nuclear@4: } nuclear@4: glUseProgram(prog); nuclear@4: return glGetError() == GL_NO_ERROR ? 0 : -1; nuclear@4: } nuclear@4: return -1; nuclear@4: } nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: /* ugly but I'm not going to write the same bloody code over and over */ nuclear@4: #define BEGIN_UNIFORM_CODE \ nuclear@4: int loc, curr_prog; \ nuclear@4: glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \ nuclear@4: if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { \ nuclear@4: return -1; \ nuclear@4: } \ nuclear@4: if((loc = glGetUniformLocation(prog, name)) != -1) nuclear@4: nuclear@4: #define END_UNIFORM_CODE \ nuclear@4: if((unsigned int)curr_prog != prog) { \ nuclear@4: bind_program(curr_prog); \ nuclear@4: } \ nuclear@4: return loc == -1 ? -1 : 0 nuclear@4: nuclear@4: int set_uniform_int(unsigned int prog, const char *name, int val) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniform1i(loc, val); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int set_uniform_float(unsigned int prog, const char *name, float val) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniform1f(loc, val); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int set_uniform_float2(unsigned int prog, const char *name, float x, float y) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniform2f(loc, x, y); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniform3f(loc, x, y, z); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniform4f(loc, x, y, z, w); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int set_uniform_matrix4(unsigned int prog, const char *name, float *mat) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniformMatrix4fv(loc, 1, GL_FALSE, mat); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat) nuclear@4: { nuclear@4: BEGIN_UNIFORM_CODE { nuclear@4: glUniformMatrix4fv(loc, 1, GL_TRUE, mat); nuclear@4: } nuclear@4: END_UNIFORM_CODE; nuclear@4: } nuclear@4: nuclear@4: int get_attrib_loc(unsigned int prog, const char *name) nuclear@4: { nuclear@4: int loc, curr_prog; nuclear@4: nuclear@4: glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); nuclear@4: if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { nuclear@4: return -1; nuclear@4: } nuclear@4: nuclear@4: loc = glGetAttribLocation(prog, (char*)name); nuclear@4: nuclear@4: if((unsigned int)curr_prog != prog) { nuclear@4: bind_program(curr_prog); nuclear@4: } nuclear@4: return loc; nuclear@4: } nuclear@4: nuclear@4: void set_attrib_float3(int attr_loc, float x, float y, float z) nuclear@4: { nuclear@4: glVertexAttrib3f(attr_loc, x, y, z); nuclear@4: } nuclear@4: nuclear@4: static const char *sdrtypestr(unsigned int sdrtype) nuclear@4: { nuclear@4: switch(sdrtype) { nuclear@4: case GL_VERTEX_SHADER: nuclear@4: return "vertex"; nuclear@4: case GL_FRAGMENT_SHADER: nuclear@4: return "pixel"; nuclear@4: #ifdef GL_TESS_CONTROL_SHADER nuclear@4: case GL_TESS_CONTROL_SHADER: nuclear@4: return "tessellation control"; nuclear@4: #endif nuclear@4: #ifdef GL_TESS_EVALUATION_SHADER nuclear@4: case GL_TESS_EVALUATION_SHADER: nuclear@4: return "tessellation evaluation"; nuclear@4: #endif nuclear@4: #ifdef GL_GEOMETRY_SHADER nuclear@4: case GL_GEOMETRY_SHADER: nuclear@4: return "geometry"; nuclear@4: #endif nuclear@4: nuclear@4: default: nuclear@4: break; nuclear@4: } nuclear@4: return ""; nuclear@4: }