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