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