nuclear@13: #include nuclear@13: #include nuclear@13: #include nuclear@13: #include nuclear@13: #include "opengl.h" nuclear@13: #include "shader.h" nuclear@13: #include "mesh.h" nuclear@13: #include "logger.h" nuclear@13: #include "unistate.h" nuclear@13: nuclear@13: #ifdef _MSC_VER nuclear@13: #include nuclear@13: #else nuclear@13: #include nuclear@13: #endif nuclear@13: nuclear@13: #ifdef __GLEW_H__ nuclear@13: #define HAVE_GEOMETRY_SHADER nuclear@13: #define HAVE_TESSELATION_SHADER nuclear@13: #endif nuclear@13: nuclear@13: static std::string read_source(const char *fname); nuclear@13: static void bind_standard_attr(const ShaderProg *prog); nuclear@13: nuclear@13: ShaderProg *ShaderProg::current; nuclear@13: nuclear@13: void bind_shader(const ShaderProg *sdr) nuclear@13: { nuclear@13: if(sdr) { nuclear@13: sdr->bind(); nuclear@13: } else { nuclear@13: #ifndef GL_ES_VERSION_2_0 nuclear@13: glUseProgram(0); nuclear@13: ShaderProg::current = 0; nuclear@13: #endif nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: const ShaderProg *get_current_shader() nuclear@13: { nuclear@13: return ShaderProg::current; nuclear@13: } nuclear@13: nuclear@13: Shader::Shader() nuclear@13: { nuclear@13: sdr = type = 0; nuclear@13: } nuclear@13: nuclear@13: Shader::~Shader() nuclear@13: { nuclear@13: destroy(); nuclear@13: } nuclear@13: nuclear@13: unsigned int Shader::get_id() const nuclear@13: { nuclear@13: return sdr; nuclear@13: } nuclear@13: nuclear@13: void Shader::set_name(const char *name) nuclear@13: { nuclear@13: this->name = std::string(name); nuclear@13: } nuclear@13: nuclear@13: const char *Shader::get_name() const nuclear@13: { nuclear@13: return name.c_str(); nuclear@13: } nuclear@13: nuclear@13: bool Shader::create(const char *src, unsigned int type) nuclear@13: { nuclear@13: #if !GL_ES_VERSION_2_0 nuclear@13: const char *src_arr[] = {src}; nuclear@13: #else nuclear@13: const char *src_arr[] = { "precision mediump float; ", src }; nuclear@13: #endif nuclear@13: nuclear@13: // keep a copy of the source code nuclear@13: this->src = std::string(src); nuclear@13: nuclear@13: if(!sdr) { nuclear@13: sdr = glCreateShader(type); nuclear@13: } nuclear@13: nuclear@13: info_log("compiling shader: %s... ", name.c_str()); nuclear@13: nuclear@13: glShaderSource(sdr, sizeof src_arr / sizeof *src_arr, src_arr, 0); nuclear@13: glCompileShader(sdr); nuclear@13: nuclear@13: int status; nuclear@13: glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); nuclear@13: nuclear@13: info_log(status ? "success\n" : "failed\n"); nuclear@13: nuclear@13: int info_len; nuclear@13: glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); nuclear@13: if(info_len > 1) { nuclear@13: char *buf = (char*)alloca(info_len); nuclear@13: glGetShaderInfoLog(sdr, info_len, 0, buf); nuclear@13: buf[info_len - 1] = 0; nuclear@13: nuclear@13: if(status) { nuclear@13: info_log("%s\n", buf); nuclear@13: } else { nuclear@13: error_log("%s\n", buf); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: return status == GL_TRUE; nuclear@13: } nuclear@13: nuclear@13: void Shader::destroy() nuclear@13: { nuclear@13: if(sdr) { nuclear@13: glDeleteShader(sdr); nuclear@13: } nuclear@13: sdr = type = 0; nuclear@13: nuclear@13: src.clear(); nuclear@13: name.clear(); nuclear@13: } nuclear@13: nuclear@13: static std::string read_source(const char *fname) nuclear@13: { nuclear@13: FILE *fp; nuclear@13: nuclear@13: if(!(fp = fopen(fname, "rb"))) { nuclear@13: error_log("failed to load shader: %s: %s\n", fname, strerror(errno)); nuclear@13: return std::string(); nuclear@13: } nuclear@13: nuclear@13: fseek(fp, 0, SEEK_END); nuclear@13: long sz = ftell(fp); nuclear@13: rewind(fp); nuclear@13: nuclear@13: char *src = (char*)alloca(sz + 1); nuclear@13: if(fread(src, 1, sz, fp) < (size_t)sz) { nuclear@13: error_log("failed to load shader: %s: %s\n", fname, strerror(errno)); nuclear@13: fclose(fp); nuclear@13: delete [] src; nuclear@13: return std::string(); nuclear@13: } nuclear@13: src[sz] = 0; nuclear@13: fclose(fp); nuclear@13: nuclear@13: return std::string(src); nuclear@13: } nuclear@13: nuclear@13: bool Shader::load(const char *fname, unsigned int type) nuclear@13: { nuclear@13: std::string src = read_source(fname); nuclear@13: if(src.empty()) { nuclear@13: return false; nuclear@13: } nuclear@13: set_name(fname); nuclear@13: return create(src.c_str(), type); nuclear@13: } nuclear@13: nuclear@13: // ---- shader program ---- nuclear@13: ShaderProg::ShaderProg() nuclear@13: { nuclear@13: prog = 0; nuclear@13: must_link = true; nuclear@13: } nuclear@13: nuclear@13: ShaderProg::~ShaderProg() nuclear@13: { nuclear@13: destroy(); nuclear@13: } nuclear@13: nuclear@13: unsigned int ShaderProg::get_id() const nuclear@13: { nuclear@13: return prog; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::create(const char *src, unsigned int type, ...) nuclear@13: { nuclear@13: va_list ap; nuclear@13: nuclear@13: va_start(ap, type); nuclear@13: bool res = create(src, type, ap); nuclear@13: va_end(ap); nuclear@13: nuclear@13: return res; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::create(const char *src, unsigned int type, va_list ap) nuclear@13: { nuclear@13: destroy(); nuclear@13: prog = glCreateProgram(); nuclear@13: nuclear@13: while(src) { nuclear@13: Shader *sdr = new Shader; nuclear@13: if(!sdr->create(src, type)) { nuclear@13: va_end(ap); nuclear@13: return false; nuclear@13: } nuclear@13: add_shader(sdr); nuclear@13: src = va_arg(ap, const char*); nuclear@13: type = va_arg(ap, unsigned int); nuclear@13: } nuclear@13: nuclear@13: return link(); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::create(const char *vsrc, const char *psrc) nuclear@13: { nuclear@13: return create(VSDR(vsrc), PSDR(psrc), 0); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::create(Shader *sdr, ...) nuclear@13: { nuclear@13: va_list ap; nuclear@13: nuclear@13: va_start(ap, sdr); nuclear@13: bool res = create(sdr, ap); nuclear@13: va_end(ap); nuclear@13: nuclear@13: return res; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::create(Shader *sdr, va_list ap) nuclear@13: { nuclear@13: destroy(); nuclear@13: prog = glCreateProgram(); nuclear@13: nuclear@13: while(sdr) { nuclear@13: add_shader(sdr); nuclear@13: sdr = va_arg(ap, Shader*); nuclear@13: } nuclear@13: return link(); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::create(Shader *vsdr, Shader *psdr) nuclear@13: { nuclear@13: return create(vsdr, psdr, 0); nuclear@13: } nuclear@13: nuclear@13: void ShaderProg::destroy() nuclear@13: { nuclear@13: if(prog) { nuclear@13: glDeleteProgram(prog); nuclear@13: } nuclear@13: prog = 0; nuclear@13: nuclear@13: shaders.clear(); nuclear@13: // don't actually destroy the shaders, let the ShaderSet own them nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::load(const char *fname, unsigned int type, ...) nuclear@13: { nuclear@13: va_list ap; nuclear@13: va_start(ap, type); nuclear@13: bool res = load(fname, type, ap); nuclear@13: va_end(ap); nuclear@13: nuclear@13: return res; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::load(const char *fname, unsigned int type, va_list ap) nuclear@13: { nuclear@13: destroy(); nuclear@13: prog = glCreateProgram(); nuclear@13: nuclear@13: while(fname) { nuclear@13: Shader *sdr = new Shader; nuclear@13: if(!sdr->load(fname, type)) { nuclear@13: delete sdr; nuclear@13: return false; nuclear@13: } nuclear@13: add_shader(sdr); nuclear@13: nuclear@13: if((fname = va_arg(ap, const char*))) { nuclear@13: type = va_arg(ap, unsigned int); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: return link(); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::load(const char *vfname, const char *pfname) nuclear@13: { nuclear@13: return load(VSDR(vfname), PSDR(pfname), 0); nuclear@13: } nuclear@13: nuclear@13: void ShaderProg::add_shader(Shader *sdr) nuclear@13: { nuclear@13: glAttachShader(prog, sdr->get_id()); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::link() const nuclear@13: { nuclear@13: bind_standard_attr(this); nuclear@13: nuclear@13: CHECKGLERR; nuclear@13: info_log("linking program ... "); nuclear@13: glLinkProgram(prog); nuclear@13: nuclear@13: int status; nuclear@13: glGetProgramiv(prog, GL_LINK_STATUS, &status); nuclear@13: nuclear@13: info_log(status ? "success\n" : "failed\n"); nuclear@13: nuclear@13: int info_len; nuclear@13: glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); nuclear@13: if(info_len > 1) { nuclear@13: char *buf = (char*)alloca(info_len); nuclear@13: glGetProgramInfoLog(prog, info_len, 0, buf); nuclear@13: buf[info_len - 1] = 0; nuclear@13: nuclear@13: if(status) { nuclear@13: info_log("%s\n", buf); nuclear@13: } else { nuclear@13: error_log("%s\n", buf); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: if(status) { nuclear@13: must_link = false; nuclear@13: cache_state_uniforms(); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: void ShaderProg::bind() const nuclear@13: { nuclear@13: CHECKGLERR; nuclear@13: if(must_link) { nuclear@13: if(!link()) { nuclear@13: return; nuclear@13: } nuclear@13: } nuclear@13: CHECKGLERR; nuclear@13: glUseProgram(prog); nuclear@13: ShaderProg::current = (ShaderProg*)this; nuclear@13: nuclear@13: setup_state_uniforms(); nuclear@13: } nuclear@13: nuclear@13: nuclear@13: int ShaderProg::get_attrib_location(const char *name) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: return glGetAttribLocation(prog, name); nuclear@13: } nuclear@13: nuclear@13: void ShaderProg::set_attrib_location(const char *name, int loc) const nuclear@13: { nuclear@13: glBindAttribLocation(prog, loc, name); nuclear@13: must_link = true; nuclear@13: } nuclear@13: nuclear@13: int ShaderProg::get_uniform_location(const char *name) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: return glGetUniformLocation(prog, name); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, int val) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniform1i(loc, val); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, float val) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniform1f(loc, val); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, const Vector2 &v) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniform2f(loc, v.x, v.y); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, const Vector3 &v) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniform3f(loc, v.x, v.y, v.z); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, const Vector4 &v) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniform4f(loc, v.x, v.y, v.z, v.w); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, const Matrix3x3 &m) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniformMatrix3fv(loc, 1, GL_TRUE, m[0]); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(int loc, const Matrix4x4 &m) const nuclear@13: { nuclear@13: glUseProgram(prog); nuclear@13: if(loc >= 0) { nuclear@13: glUniformMatrix4fv(loc, 1, GL_TRUE, m[0]); nuclear@13: return true; nuclear@13: } nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, int val) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), val); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, float val) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), val); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, const Vector2 &v) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), v); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, const Vector3 &v) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), v); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, const Vector4 &v) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), v); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, const Matrix3x3 &m) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), m); nuclear@13: } nuclear@13: nuclear@13: bool ShaderProg::set_uniform(const char *name, const Matrix4x4 &m) const nuclear@13: { nuclear@13: return set_uniform(get_uniform_location(name), m); nuclear@13: } nuclear@13: nuclear@13: static StType unist_type(GLenum type) nuclear@13: { nuclear@13: switch(type) { nuclear@13: case GL_FLOAT: nuclear@13: return ST_FLOAT; nuclear@13: case GL_FLOAT_VEC2: nuclear@13: return ST_FLOAT2; nuclear@13: case GL_FLOAT_VEC3: nuclear@13: return ST_FLOAT3; nuclear@13: case GL_FLOAT_VEC4: nuclear@13: return ST_FLOAT4; nuclear@13: case GL_INT: nuclear@13: case GL_SAMPLER_2D: nuclear@13: case GL_SAMPLER_CUBE: nuclear@13: #if !GL_ES_VERSION_2_0 nuclear@13: case GL_SAMPLER_1D: nuclear@13: case GL_SAMPLER_3D: nuclear@13: case GL_SAMPLER_1D_SHADOW: nuclear@13: case GL_SAMPLER_2D_SHADOW: nuclear@13: #endif nuclear@13: return ST_INT; nuclear@13: case GL_INT_VEC2: nuclear@13: return ST_INT2; nuclear@13: case GL_INT_VEC3: nuclear@13: return ST_INT3; nuclear@13: case GL_INT_VEC4: nuclear@13: return ST_INT4; nuclear@13: case GL_FLOAT_MAT3: nuclear@13: return ST_MATRIX3; nuclear@13: case GL_FLOAT_MAT4: nuclear@13: return ST_MATRIX4; nuclear@13: default: nuclear@13: break; nuclear@13: } nuclear@13: return ST_UNKNOWN; nuclear@13: } nuclear@13: nuclear@13: void ShaderProg::cache_state_uniforms() const nuclear@13: { nuclear@13: if(!glIsProgram(prog)) { nuclear@13: return; nuclear@13: } nuclear@13: nuclear@13: int num_uni; nuclear@13: glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &num_uni); nuclear@13: nuclear@13: char name[256]; nuclear@13: for(int i=0; iload(fname, type)) { nuclear@13: delete sdr; nuclear@13: return 0; nuclear@13: } nuclear@13: return sdr; nuclear@13: } nuclear@13: nuclear@13: static Shader *load_vertex_shader(const char *fname) nuclear@13: { nuclear@13: return load_shader(fname, GL_VERTEX_SHADER); nuclear@13: } nuclear@13: nuclear@13: static Shader *load_pixel_shader(const char *fname) nuclear@13: { nuclear@13: return load_shader(fname, GL_FRAGMENT_SHADER); nuclear@13: } nuclear@13: nuclear@13: #ifdef HAVE_GEOMETRY_SHADER nuclear@13: static Shader *load_geom_shader(const char *fname) nuclear@13: { nuclear@13: return load_shader(fname, GL_GEOMETRY_SHADER); nuclear@13: } nuclear@13: #endif nuclear@13: nuclear@13: #ifdef HAVE_TESSELATION_SHADER nuclear@13: static Shader *load_tc_shader(const char *fname) nuclear@13: { nuclear@13: return load_shader(fname, GL_TESS_CONTROL_SHADER); nuclear@13: } nuclear@13: nuclear@13: static Shader *load_te_shader(const char *fname) nuclear@13: { nuclear@13: return load_shader(fname, GL_TESS_EVALUATION_SHADER); nuclear@13: } nuclear@13: #endif nuclear@13: nuclear@13: static void destroy_shader(Shader *sdr) nuclear@13: { nuclear@13: delete sdr; nuclear@13: } nuclear@13: nuclear@13: ShaderSet::ShaderSet(unsigned int type) nuclear@13: : DataSet(0, destroy_shader) nuclear@13: { nuclear@13: this->type = type; nuclear@13: nuclear@13: switch(type) { nuclear@13: case GL_VERTEX_SHADER: nuclear@13: load = load_vertex_shader; nuclear@13: break; nuclear@13: nuclear@13: case GL_FRAGMENT_SHADER: nuclear@13: load = load_pixel_shader; nuclear@13: break; nuclear@13: nuclear@13: #ifdef HAVE_GEOMETRY_SHADER nuclear@13: case GL_GEOMETRY_SHADER: nuclear@13: load = load_geom_shader; nuclear@13: break; nuclear@13: #endif nuclear@13: nuclear@13: #ifdef HAVE_TESSELATION_SHADER nuclear@13: case GL_TESS_CONTROL_SHADER: nuclear@13: load = load_tc_shader; nuclear@13: break; nuclear@13: nuclear@13: case GL_TESS_EVALUATION_SHADER: nuclear@13: load = load_te_shader; nuclear@13: break; nuclear@13: #endif nuclear@13: nuclear@13: default: nuclear@13: error_log("ShaderSet constructed with invalid shader type!\n"); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: static struct { const char *name; int loc; } attr_loc[] = { nuclear@13: {"attr_vertex", MESH_ATTR_VERTEX}, nuclear@13: {"attr_normal", MESH_ATTR_NORMAL}, nuclear@13: {"attr_tangent", MESH_ATTR_TANGENT}, nuclear@13: {"attr_texcoord", MESH_ATTR_TEXCOORD}, nuclear@13: {"attr_color", MESH_ATTR_COLOR}, nuclear@13: {"attr_boneweights", MESH_ATTR_BONEWEIGHTS}, nuclear@13: {"attr_boneidx", MESH_ATTR_BONEIDX} nuclear@13: }; nuclear@13: nuclear@13: static void bind_standard_attr(const ShaderProg *prog) nuclear@13: { nuclear@13: // we must link once to find out which are the active attributes nuclear@13: glLinkProgram(prog->get_id()); nuclear@13: nuclear@13: int num_attr; nuclear@13: glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr); nuclear@13: nuclear@13: char name[256]; nuclear@13: for(int i=0; iget_id(), i, sizeof name - 1, 0, &sz, &type, name); nuclear@13: nuclear@13: for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) { nuclear@13: if(strcmp(name, attr_loc[j].name) == 0) { nuclear@13: prog->set_attrib_location(name, attr_loc[j].loc); nuclear@13: } nuclear@13: } nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: nuclear@13: /* nuclear@13: static const char *strtype(unsigned int type) nuclear@13: { nuclear@13: switch(type) { nuclear@13: case GL_VERTEX_SHADER: nuclear@13: return "vertex"; nuclear@13: case GL_FRAGMENT_SHADER: nuclear@13: return "fragment"; nuclear@13: #ifdef HAVE_GEOMETRY_SHADER nuclear@13: case GL_GEOMETRY_SHADER: nuclear@13: return "geometry"; nuclear@13: #endif nuclear@13: #ifdef HAVE_TESSELATION_SHADER nuclear@13: case GL_TESS_CONTROL_SHADER: nuclear@13: return "tesselation control"; nuclear@13: case GL_TESS_EVALUATION_SHADER: nuclear@13: return "tesselation evaluation"; nuclear@13: #endif nuclear@13: default: nuclear@13: break; nuclear@13: } nuclear@13: return ""; nuclear@13: } nuclear@13: */