From bbe228a794ac07f8dbadeef553b685f23fe5503d Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sun, 25 Dec 2016 05:03:55 +0200 Subject: [PATCH] added shader class and unistate. not using shader class yet --- src/shader.cc | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/shader.h | 148 ++++++++++++ src/unistate.cc | 677 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/unistate.h | 104 ++++++++ 4 files changed, 1645 insertions(+) create mode 100644 src/shader.cc create mode 100644 src/shader.h create mode 100644 src/unistate.cc create mode 100644 src/unistate.h diff --git a/src/shader.cc b/src/shader.cc new file mode 100644 index 0000000..d7f863a --- /dev/null +++ b/src/shader.cc @@ -0,0 +1,716 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "shader.h" +#include "logger.h" +#include "unistate.h" +#include "mesh.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef __GLEW_H__ +#define HAVE_GEOMETRY_SHADER +#define HAVE_TESSELATION_SHADER +#endif + +static void bind_standard_attr(const ShaderProg *prog); +static const char *strtype(unsigned int type); + +ShaderProg *ShaderProg::current; + +void bind_shader(const ShaderProg *sdr) +{ + if(sdr) { + sdr->bind(); + } else { +#ifndef GL_ES_VERSION_2_0 + glUseProgram(0); + ShaderProg::current = 0; +#endif + } +} + +const ShaderProg *get_current_shader() +{ + return ShaderProg::current; +} + + +Shader::Shader() +{ + sdr = type = 0; + name = src = 0; +} + +Shader::~Shader() +{ + destroy(); +} + +unsigned int Shader::get_id() const +{ + return sdr; +} + +unsigned int Shader::get_type() const +{ + return type; +} + +void Shader::set_name(const char *name) +{ + delete [] this->name; + this->name = new char[strlen(name) + 1]; + strcpy(this->name, name); +} + +const char *Shader::get_name() const +{ + return name; +} + +void Shader::set_source(const char *src) +{ + delete [] this->src; + this->src = new char[strlen(src) + 1]; + strcpy(this->src, src); +} + +const char *Shader::get_source() const +{ + return src; +} + +bool Shader::create(const char *src, unsigned int type) +{ +#if !GL_ES_VERSION_2_0 + const char *src_arr[] = {src}; +#else + const char *src_arr[] = { "precision mediump float; ", src }; +#endif + this->type = type; + + if(src != this->src) { + delete [] this->src; + int len = strlen(src); + this->src = new char[len + 1]; + memcpy(this->src, src, len + 1); + } + + if(!sdr) { + sdr = glCreateShader(type); + } + + info_log("compiling shader: %s... ", name ? name : ""); + + glShaderSource(sdr, sizeof src_arr / sizeof *src_arr, src_arr, 0); + + glCompileShader(sdr); + + int status; + glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); + + info_log(status ? "success\n" : "failed\n"); + + int info_len; + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + if(info_len > 1) { + char *buf = (char*)alloca(info_len); + glGetShaderInfoLog(sdr, info_len, 0, buf); + buf[info_len - 1] = 0; + + if(status) { + info_log("%s\n", buf); + } else { + error_log("%s\n", buf); + } + } + + return status == GL_TRUE; +} + +void Shader::destroy() +{ + if(sdr) { + glDeleteShader(sdr); + } + sdr = type = 0; + delete [] src; + src = 0; + delete [] name; + name = 0; +} + +bool Shader::load(const char *fname, unsigned int type) +{ + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); + return false; + } + + fseek(fp, 0, SEEK_END); + long sz = ftell(fp); + fseek(fp, 0, SEEK_SET); + + char *src = (char*)alloca(sz + 1); + if(fread(src, 1, sz, fp) < (size_t)sz) { + error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); + fclose(fp); + return false; + } + src[sz] = 0; + fclose(fp); + + set_name(fname); + return create(src, type); +} + +// ---- shader program ---- +ShaderProg::ShaderProg() +{ + prog = 0; + must_link = true; +} + +ShaderProg::~ShaderProg() +{ + destroy(); +} + +unsigned int ShaderProg::get_id() const +{ + return prog; +} + +bool ShaderProg::create(const char *src, unsigned int type, ...) +{ + va_list ap; + + va_start(ap, type); + bool res = create(src, type, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::create(const char *src, unsigned int type, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(src) { + Shader *sdr = new Shader; + if(!sdr->create(src, type)) { + delete sdr; + return false; + } + add_shader(sdr); + src = va_arg(ap, const char*); + type = va_arg(ap, unsigned int); + } + return link(); +} + +bool ShaderProg::create(const char *vsrc, const char *psrc) +{ + return create(VSDR(vsrc), PSDR(psrc), 0); +} + +bool ShaderProg::create(Shader *sdr, ...) +{ + va_list ap; + + va_start(ap, sdr); + bool res = create(sdr, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::create(Shader *sdr, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(sdr) { + add_shader(sdr); + sdr = va_arg(ap, Shader*); + } + return link(); +} + +bool ShaderProg::create(Shader *vsdr, Shader *psdr) +{ + return create(vsdr, psdr, 0); +} + +void ShaderProg::destroy() +{ + if(prog) { + glDeleteProgram(prog); + } + prog = 0; + + shaders.clear(); + // don't actually destroy the shaders, let the ShaderSet own them +} + +bool ShaderProg::load(const char *fname, unsigned int type, ...) +{ + va_list ap; + va_start(ap, type); + bool res = load(fname, type, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::load(const char *fname, unsigned int type, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(fname) { + Shader *sdr = new Shader; + if(!sdr->load(fname, type)) { + delete sdr; + return false; + } + add_shader(sdr); + + if((fname = va_arg(ap, const char*))) { + type = va_arg(ap, unsigned int); + } + } + + return link(); +} + +bool ShaderProg::load(const char *vfname, const char *pfname) +{ + return load(VSDR(vfname), PSDR(pfname), 0); +} + +void ShaderProg::add_shader(Shader *sdr) +{ + glAttachShader(prog, sdr->get_id()); +} + +bool ShaderProg::link() const +{ + bind_standard_attr(this); + + info_log("linking program ... "); + glLinkProgram(prog); + + int status; + glGetProgramiv(prog, GL_LINK_STATUS, &status); + + info_log(status ? "success\n" : "failed\n"); + + int info_len; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + if(info_len > 1) { + char *buf = (char*)alloca(info_len); + glGetProgramInfoLog(prog, info_len, 0, buf); + buf[info_len - 1] = 0; + + if(status) { + info_log("%s\n", buf); + } else { + error_log("%s\n", buf); + } + } + + if(status) { + must_link = false; + cache_state_uniforms(); + + return true; + } + return false; +} + +void ShaderProg::bind() const +{ + if(must_link) { + if(!link()) { + return; + } + } + glUseProgram(prog); + ShaderProg::current = (ShaderProg*)this; + + setup_state_uniforms(); +} + + +int ShaderProg::get_attrib_location(const char *name) const +{ + bind(); + return glGetAttribLocation(prog, name); +} + +void ShaderProg::set_attrib_location(const char *name, int loc) const +{ + glBindAttribLocation(prog, loc, name); + must_link = true; +} + +int ShaderProg::get_uniform_location(const char *name) const +{ + bind(); + return glGetUniformLocation(prog, name); +} + +bool ShaderProg::set_uniform(int loc, int val) const +{ + bind(); + if(loc >= 0) { + glUniform1i(loc, val); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, float val) const +{ + bind(); + if(loc >= 0) { + glUniform1f(loc, val); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vec2 &v) const +{ + bind(); + if(loc >= 0) { + glUniform2f(loc, v.x, v.y); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vec3 &v) const +{ + bind(); + if(loc >= 0) { + glUniform3f(loc, v.x, v.y, v.z); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vec4 &v) const +{ + bind(); + if(loc >= 0) { + glUniform4f(loc, v.x, v.y, v.z, v.w); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Mat3 &m) const +{ + bind(); + if(loc >= 0) { + glUniformMatrix3fv(loc, 1, GL_FALSE, m[0]); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Mat4 &m) const +{ + bind(); + if(loc >= 0) { + glUniformMatrix4fv(loc, 1, GL_FALSE, m[0]); + return true; + } + return false; +} + + +bool ShaderProg::set_uniform(const char *name, int val) const +{ + return set_uniform(get_uniform_location(name), val); +} + +bool ShaderProg::set_uniform(const char *name, float val) const +{ + return set_uniform(get_uniform_location(name), val); +} + +bool ShaderProg::set_uniform(const char *name, const Vec2 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Vec3 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Vec4 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Mat3 &m) const +{ + return set_uniform(get_uniform_location(name), m); +} + +bool ShaderProg::set_uniform(const char *name, const Mat4 &m) const +{ + return set_uniform(get_uniform_location(name), m); +} + +static StType unist_type(GLenum type) +{ + switch(type) { + case GL_FLOAT: + return ST_FLOAT; + case GL_FLOAT_VEC2: + return ST_FLOAT2; + case GL_FLOAT_VEC3: + return ST_FLOAT3; + case GL_FLOAT_VEC4: + return ST_FLOAT4; + case GL_INT: + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: +#if !GL_ES_VERSION_2_0 + case GL_SAMPLER_1D: + case GL_SAMPLER_3D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: +#endif + return ST_INT; + case GL_INT_VEC2: + return ST_INT2; + case GL_INT_VEC3: + return ST_INT3; + case GL_INT_VEC4: + return ST_INT4; + case GL_FLOAT_MAT3: + return ST_MATRIX3; + case GL_FLOAT_MAT4: + return ST_MATRIX4; + default: + break; + } + return ST_UNKNOWN; +} + +void ShaderProg::cache_state_uniforms() const +{ + if(!glIsProgram(prog)) { + return; + } + + int num_uni; + glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &num_uni); + + char name[256]; + for(int i=0; isrc = new char[sz + 1]; + if(fread(sdr->src, 1, sz, fp) < (size_t)sz) { + error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); + fclose(fp); + delete [] sdr->src; + return false; + } + sdr->src[sz] = 0; + fclose(fp); + + sdr->set_name(fname); + return true; +} + +static bool load_vertex_shader(Shader *sdr, const char *fname) +{ + return load_shader(sdr, fname, GL_VERTEX_SHADER); +} + +static bool load_pixel_shader(Shader *sdr, const char *fname) +{ + return load_shader(sdr, fname, GL_FRAGMENT_SHADER); +} + +#ifdef HAVE_GEOMETRY_SHADER +static bool load_geom_shader(Shader *sdr, const char *fname) +{ + return load_shader(sdr, fname, GL_GEOMETRY_SHADER); +} +#endif + +#ifdef HAVE_TESSELATION_SHADER +static bool load_tc_shader(Shader *sdr, const char *fname) +{ + return load_shader(sdr, fname, GL_TESS_CONTROL_SHADER); +} + +static bool load_te_shader(Shader *sdr, const char *fname) +{ + return load_shader(sdr, fname, GL_TESS_EVALUATION_SHADER); +} +#endif + +static bool done_shader(Shader *sdr) +{ + return sdr->create(sdr->get_source(), sdr->get_type()); +} + +static void destroy_shader(Shader *sdr) +{ + delete sdr; +} + +ShaderSet::ShaderSet(unsigned int type) + : DataSet(create_shader, 0, done_shader, destroy_shader) +{ + this->type = type; + + switch(type) { + case GL_VERTEX_SHADER: + load = load_vertex_shader; + break; + + case GL_FRAGMENT_SHADER: + load = load_pixel_shader; + break; + +#ifdef HAVE_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + load = load_geom_shader; + break; +#endif + +#ifdef HAVE_TESSELATION_SHADER + case GL_TESS_CONTROL_SHADER: + load = load_tc_shader; + break; + + case GL_TESS_EVALUATION_SHADER: + load = load_te_shader; + break; +#endif + + default: + error_log("ShaderSet constructed with invalid shader type!\n"); + } +} + +static struct { + const char *name; + int loc; +} attr_loc[] = { + {"attr_vertex", MESH_ATTR_VERTEX}, + {"attr_normal", MESH_ATTR_NORMAL}, + {"attr_tangent", MESH_ATTR_TANGENT}, + {"attr_texcoord", MESH_ATTR_TEXCOORD}, + {"attr_color", MESH_ATTR_COLOR}, + {"attr_boneweights", MESH_ATTR_BONEWEIGHTS}, + {"attr_boneidx", MESH_ATTR_BONEIDX} +}; + +static void bind_standard_attr(const ShaderProg *prog) +{ + // we must link once to find out which are the active attributes + glLinkProgram(prog->get_id()); + + int num_attr; + glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr); + + char name[256]; + for(int i=0; iget_id(), i, sizeof name - 1, 0, &sz, &type, name); + + for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) { + if(strcmp(name, attr_loc[j].name) == 0) { + prog->set_attrib_location(name, attr_loc[j].loc); + } + } + } +} + + +static const char *strtype(unsigned int type) +{ + switch(type) { + case GL_VERTEX_SHADER: + return "vertex"; + case GL_FRAGMENT_SHADER: + return "fragment"; +#ifdef HAVE_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + return "geometry"; +#endif +#ifdef HAVE_TESSELATION_SHADER + case GL_TESS_CONTROL_SHADER: + return "tesselation control"; + case GL_TESS_EVALUATION_SHADER: + return "tesselation evaluation"; +#endif + default: + break; + } + return ""; +} diff --git a/src/shader.h b/src/shader.h new file mode 100644 index 0000000..b44e0e2 --- /dev/null +++ b/src/shader.h @@ -0,0 +1,148 @@ +#ifndef SHADER_H_ +#define SHADER_H_ + +#include +#include +#include "opengl.h" +#include "dataset.h" + +class ShaderProg; + + +void bind_shader(const ShaderProg *sdr); +const ShaderProg *get_current_shader(); + + +class Shader { +private: + unsigned int sdr; + unsigned int type; + char *name, *src; + +public: + Shader(); + ~Shader(); + + unsigned int get_id() const; + unsigned int get_type() const; + + void set_name(const char *name); + const char *get_name() const; + + void set_source(const char *src); + const char *get_source() const; + + bool create(const char *src, unsigned int type); + void destroy(); + + bool load(const char *fname, unsigned int type); + + friend bool load_shader(Shader*, const char*, unsigned int); +}; + +#define VSDR(s) s, GL_VERTEX_SHADER +#define FSDR(s) s, GL_FRAGMENT_SHADER +#define PSDR(s) FSDR(s) +#define GSDR(s) s, GL_GEOMETRY_SHADER +#define TCSDR(s) s, GL_TESS_CONTROL_SHADER +#define TESDR(s) s, GL_TESS_EVALUATION_SHADER + +class ShaderProg { +private: + unsigned int prog; + mutable bool must_link; + std::vector shaders; + + struct StateLocCache { int sidx, loc; }; + /** a cache of all st_ prefixed uniform locations and their corresponding + * index in the global uniform state vector (see unistate.h) + */ + mutable std::vector stloc_cache; + + void cache_state_uniforms() const; + void setup_state_uniforms() const; + +public: + static ShaderProg *current; + + ShaderProg(); + ~ShaderProg(); + + /// returns the OpenGL object id for this shader program + unsigned int get_id() const; + + /** takes a series of shaders, and constructs a program object by linking + * them together. Terminate with a null pointer (don't use 0!) */ + bool create(Shader *sdr, ...); + /// same as above, but with a va_list instead of variable arguments. + bool create(Shader *sdr, va_list ap); + /** takes two shaders (vertex and pixel) and constructs a program object by + * linking them together. Either one can be null. */ + bool create(Shader *vsdr, Shader *psdr); + + /** takes a series of shader source/shader type pairs and constructs a program + * object by linking them together. Terminate with a null pointer (don't use 0!) + * You can use the VSDR, PSDR, GSDR, TCSDR, TESDR convenience macros for passing + * the pairs. + * Example: create(VSDR(vsrc0), VSDR(vsrc1), PSDR(psrc), NULL); + */ + bool create(const char *src, unsigned int type, ...); + /// same as above, but with a va_list instead of variable arguments. + bool create(const char *src, unsigned int type, va_list ap); + /** takes two shaders source strings (vertex and pixel) and constructs + * a program object by linking them together. Either one can be null. */ + bool create(const char *vsrc, const char *psrc); + + void destroy(); + + /** takes a series of shader filename/shader type pairs, loads the shaders and + * constructs a program object by linking them together. Terminate with a null + * pointer (don't use 0!). You can use the VSDR, PSDR, GSDR, TCSDR, TESDR convenience + * macros for passing the pairs. + * Example: load(VSDR("vsdr1.glsl"), VSDR("vsdr2.glsl"), PSDR("pixel.glsl"), NULL); + */ + bool load(const char *fname, unsigned int type, ...); + /// same as above, but with a va_list instead of variable arguments. + bool load(const char *fname, unsigned int type, va_list ap); + /** takes the filenames of two shader files (vertex and pixel), loads them and + * constructs a program object by linking them together. Either one can be null */ + bool load(const char *vsrc, const char *psrc); + + void add_shader(Shader *sdr); + bool link() const; + + void bind() const; + + int get_attrib_location(const char *name) const; + void set_attrib_location(const char *name, int loc) const; + + int get_uniform_location(const char *name) const; + + bool set_uniform(int loc, int val) const; + bool set_uniform(int loc, float val) const; + bool set_uniform(int loc, const Vec2 &v) const; + bool set_uniform(int loc, const Vec3 &v) const; + bool set_uniform(int loc, const Vec4 &v) const; + bool set_uniform(int loc, const Mat3 &m) const; + bool set_uniform(int loc, const Mat4 &m) const; + + bool set_uniform(const char *name, int val) const; + bool set_uniform(const char *name, float val) const; + bool set_uniform(const char *name, const Vec2 &v) const; + bool set_uniform(const char *name, const Vec3 &v) const; + bool set_uniform(const char *name, const Vec4 &v) const; + bool set_uniform(const char *name, const Mat3 &m) const; + bool set_uniform(const char *name, const Mat4 &m) const; + + friend void setup_unistate(const ShaderProg*); +}; + +class ShaderSet : public DataSet { +private: + unsigned int type; + +public: + ShaderSet(unsigned int type); +}; + +#endif // SHADER_H_ diff --git a/src/unistate.cc b/src/unistate.cc new file mode 100644 index 0000000..9793501 --- /dev/null +++ b/src/unistate.cc @@ -0,0 +1,677 @@ +#include +#include +#include "unistate.h" +#include "shader.h" +#include "logger.h" + +struct StateItem { + StType type; + + union { + int ival[4]; + float fval[16]; + }; + int transpose; // for matrices +}; + +static const char *typestr(StType type); +static int type_nelem(StType type); +static StType float_type(int elem); +static StType int_type(int elem); + +std::vector state; +std::map stateidx; + + +int add_unistate(const char *name, StType type) +{ + static const float ident3[] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + static const float ident4[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + + if(stateidx.find(name) != stateidx.end()) { + return stateidx[name]; + } + + StateItem sitem; + memset(&sitem, 0, sizeof sitem); + sitem.type = type; + + // initialize to a reasonable default value + switch(type) { + case ST_MATRIX3: + memcpy(sitem.fval, ident3, sizeof ident3); + break; + + case ST_MATRIX4: + memcpy(sitem.fval, ident4, sizeof ident4); + break; + + default: + break; // in all other cases leave it zero (see memset above) + } + + int sidx = state.size(); + state.push_back(sitem); + stateidx[name] = sidx; + + debug_log("adding uniform state [%d]: %s %s\n", sidx, typestr(sitem.type), name); + + return sidx; +} + +int get_unistate_index(const char *name) +{ + std::map::const_iterator it = stateidx.find(name); + if(it != stateidx.end()) { + return it->second; + } + return -1; +} + +#define CHECK_INDEX(i) \ + if(i < 0 || i >= (int)state.size()) return + +#define CHECK_COUNT(count, type) \ + do { \ + int max_elem = type_nelem(type); \ + if(!(count) || (count) > max_elem) { \ + count = max_elem; \ + } \ + } while(0) + +void set_unistate(int sidx, const int *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(state[sidx].ival, val, count * sizeof *state[sidx].ival); +} + +void set_unistate(int sidx, const float *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(state[sidx].fval, val, count * sizeof *state[sidx].fval); + state[sidx].transpose = 0; +} + +void get_unistate(int sidx, int *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(val, state[sidx].ival, count * sizeof *val); +} + +void get_unistate(int sidx, float *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(val, state[sidx].fval, count * sizeof *val); +} + +void set_unistate(int sidx, int val) +{ + set_unistate(sidx, &val, 1); +} + +void set_unistate(int sidx, float val) +{ + set_unistate(sidx, &val, 1); +} + +void set_unistate(int sidx, const Vec2 &vec) +{ + set_unistate(sidx, &vec.x, 2); +} + +void set_unistate(int sidx, const Vec3 &vec) +{ + set_unistate(sidx, &vec.x, 3); +} + +void set_unistate(int sidx, const Vec4 &vec) +{ + set_unistate(sidx, &vec.x, 4); +} + +void set_unistate(int sidx, const Mat3 &mat) +{ + set_unistate(sidx, mat[0], 9); + state[sidx].transpose = 0; +} + +void set_unistate(int sidx, const Mat4 &mat) +{ + set_unistate(sidx, mat[0], 16); + state[sidx].transpose = 0; +} + + +int set_unistate(const char *name, int *val, int count) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + StType type = int_type(count); + if(type == ST_UNKNOWN) { + error_log("invalid element count (%d) while setting previously unknown unistate item \"%s\"\n", + count, name); + return -1; + } + + sidx = add_unistate(name, type); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, float *val, int count) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + StType type = float_type(count); + if(type == ST_UNKNOWN) { + error_log("invalid element count (%d) while setting previously unknown unistate item \"%s\"\n", + count, name); + return -1; + } + + sidx = add_unistate(name, type); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, int val) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_INT); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, float val) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, const Vec2 &vec) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT2); + } + set_unistate(sidx, vec); + return sidx; +} + +int set_unistate(const char *name, const Vec3 &vec) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT3); + } + set_unistate(sidx, vec); + return sidx; +} + +int set_unistate(const char *name, const Vec4 &vec) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT4); + } + set_unistate(sidx, vec); + return sidx; +} + +int set_unistate(const char *name, const Mat3 &mat) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_MATRIX3); + } + set_unistate(sidx, mat); + return sidx; +} + +int set_unistate(const char *name, const Mat4 &mat) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_MATRIX4); + } + set_unistate(sidx, mat); + return sidx; +} + + +int get_unistate_int(int sidx) +{ + int val = 0; + get_unistate(sidx, &val, 1); + return val; +} + +float get_unistate_float(int sidx) +{ + float val = 0.0f; + get_unistate(sidx, &val, 1); + return val; +} + +Vec2 get_unistate_vec2(int sidx) +{ + float val[2] = {0.0f, 0.0f}; + get_unistate(sidx, val, 2); + return Vec2(val[0], val[1]); +} + +Vec3 get_unistate_vec3(int sidx) +{ + float val[3] = {0.0f, 0.0f, 0.0f}; + get_unistate(sidx, val, 3); + return Vec3(val[0], val[1], val[2]); +} + +Vec4 get_unistate_vec4(int sidx) +{ + float val[4] = {0.0f, 0.0f, 0.0f}; + get_unistate(sidx, val, 4); + return Vec4(val[0], val[1], val[2], val[3]); +} + +Mat3 get_unistate_mat3(int sidx) +{ + Mat3 res; + get_unistate(sidx, res.m[0], 9); + return res; +} + +Mat4 get_unistate_mat4(int sidx) +{ + Mat4 res; + get_unistate(sidx, res.m[0], 16); + return res; +} + + +int get_unistate_int(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return 0; + } + return get_unistate_int(sidx); +} + +float get_unistate_float(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return 0.0f; + } + return get_unistate_float(sidx); +} + +Vec2 get_unistate_vec2(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Vec2(); + } + return get_unistate_vec2(sidx); +} + +Vec3 get_unistate_vec3(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Vec3(); + } + return get_unistate_vec3(sidx); +} + +Vec4 get_unistate_vec4(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Vec4(); + } + return get_unistate_vec4(sidx); +} + +Mat3 get_unistate_mat3(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Mat3(); + } + return get_unistate_mat3(sidx); +} + +Mat4 get_unistate_mat4(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Mat4(); + } + return get_unistate_mat4(sidx); +} + + +void setup_unistate(const ShaderProg *sdr) +{ + if(!sdr) { + if(!(sdr = ShaderProg::current)) { + return; + } + } + + sdr->setup_state_uniforms(); +} + +bool setup_unistate(int sidx, const ShaderProg *sdr, int loc) +{ + if(loc < 0 || sidx < 0 || sidx >= (int)state.size()) { + return false; + } + + glUseProgram(sdr->get_id()); + + switch(state[sidx].type) { + case ST_INT: + glUniform1iv(loc, 1, state[sidx].ival); + break; + case ST_INT2: + glUniform2iv(loc, 1, state[sidx].ival); + break; + case ST_INT3: + glUniform3iv(loc, 1, state[sidx].ival); + break; + case ST_INT4: + glUniform4iv(loc, 1, state[sidx].ival); + break; + + case ST_FLOAT: + glUniform1fv(loc, 1, state[sidx].fval); + break; + case ST_FLOAT2: + glUniform2fv(loc, 1, state[sidx].fval); + break; + case ST_FLOAT3: + glUniform3fv(loc, 1, state[sidx].fval); + break; + case ST_FLOAT4: + glUniform4fv(loc, 1, state[sidx].fval); + break; + + case ST_MATRIX3: +#ifdef GL_ES_VERSION_2_0 + { + float tmat[9], *ptr = tmat; + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + *ptr++ = state[sidx].fval[j * 3 + i]; + } + } + glUniformMatrix3fv(loc, 1, GL_FALSE, tmat); + } +#else + glUniformMatrix3fv(loc, 1, state[sidx].transpose, state[sidx].fval); +#endif + break; + + case ST_MATRIX4: +#ifdef GL_ES_VERSION_2_0 + { + float tmat[16], *ptr = tmat; + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + *ptr++ = state[sidx].fval[j * 4 + i]; + } + } + glUniformMatrix4fv(loc, 1, GL_FALSE, tmat); + } +#else + glUniformMatrix4fv(loc, 1, state[sidx].transpose, state[sidx].fval); +#endif + break; + + default: + return false; + } + + return true; +} + +bool setup_unistate(const char *name, const ShaderProg *sdr) +{ + int loc = sdr->get_uniform_location(name); + if(loc == -1) { + return false; + } + return setup_unistate(get_unistate_index(name), sdr, loc); +} + +void set_world_matrix(const Mat4 &mat) +{ + static int sidx = -1, sidx_transp, sidx_mat3; + + if(sidx == -1) { + sidx = add_unistate("st_world_matrix", ST_MATRIX4); + sidx_mat3 = add_unistate("st_world_matrix3", ST_MATRIX3); + sidx_transp = add_unistate("st_world_matrix_transpose", ST_MATRIX4); + } + + set_unistate(sidx, mat); + set_unistate(sidx_mat3, mat.submatrix(3, 3)); + set_unistate(sidx_transp, mat[0]); + state[sidx_transp].transpose = 1; +} + +void set_view_matrix(const Mat4 &mat) +{ + static int sidx = -1, sidx_transp, sidx_mat3; + + if(sidx == -1) { + sidx = add_unistate("st_view_matrix", ST_MATRIX4); + sidx_mat3 = add_unistate("st_view_matrix3", ST_MATRIX3); + sidx_transp = add_unistate("st_view_matrix_transpose", ST_MATRIX4); + } + + set_unistate(sidx, mat); + set_unistate(sidx_mat3, mat.submatrix(3, 3)); + set_unistate(sidx_transp, mat[0]); + state[sidx_transp].transpose = 1; +} + +void set_projection_matrix(const Mat4 &mat) +{ + static int sidx = -1; + + if(sidx == -1) { + sidx = add_unistate("st_proj_matrix", ST_MATRIX4); + } + + set_unistate(sidx, mat); +} + +void set_texture_matrix(const Mat4 &mat) +{ + static int sidx = -1; + + if(sidx == -1) { + sidx = add_unistate("st_tex_matrix", ST_MATRIX4); + } + + set_unistate(sidx, mat); +} + +Mat4 get_world_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_world_matrix")) == -1) { + return Mat4(); + } + } + return get_unistate_mat4(sidx); +} + +Mat4 get_view_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_view_matrix")) == -1) { + return Mat4(); + } + } + return get_unistate_mat4(sidx); +} + +Mat4 get_projection_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_proj_matrix")) == -1) { + return Mat4(); + } + } + return get_unistate_mat4(sidx); +} + +Mat4 get_texture_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_tex_matrix")) == -1) { + return Mat4(); + } + } + return get_unistate_mat4(sidx); +} + +void setup_gl_matrices() +{ +#ifdef USE_OLDGL + Mat4 modelview = get_view_matrix() * get_world_matrix(); + Mat4 proj = get_projection_matrix(); + Mat4 tex = get_texture_matrix(); + + glMatrixMode(GL_TEXTURE); + glLoadTransposeMatrixf(tex[0]); + glMatrixMode(GL_PROJECTION); + glLoadTransposeMatrixf(proj[0]); + glMatrixMode(GL_MODELVIEW); + glLoadTransposeMatrixf(modelview[0]); +#endif +} + +static const char *typestr(StType type) +{ + switch(type) { + case ST_INT: + return "int"; + case ST_INT2: + return "ivec2"; + case ST_INT3: + return "ivec3"; + case ST_INT4: + return "ivec4"; + case ST_FLOAT: + return "float"; + case ST_FLOAT2: + return "vec2"; + case ST_FLOAT3: + return "vec3"; + case ST_FLOAT4: + return "vec4"; + case ST_MATRIX3: + return "mat3"; + case ST_MATRIX4: + return "mat4"; + + default: + break; + } + return ""; +} + +static int type_nelem(StType type) +{ + switch(type) { + case ST_INT: + case ST_FLOAT: + return 1; + case ST_INT2: + case ST_FLOAT2: + return 2; + case ST_INT3: + case ST_FLOAT3: + return 3; + case ST_INT4: + case ST_FLOAT4: + return 4; + case ST_MATRIX3: + return 9; + case ST_MATRIX4: + return 16; + + default: + break; + } + + return 0; +} + +static StType float_type(int elem) +{ + switch(elem) { + case 1: + return ST_FLOAT; + case 2: + return ST_FLOAT2; + case 3: + return ST_FLOAT3; + case 4: + return ST_FLOAT4; + case 9: + return ST_MATRIX3; + case 16: + return ST_MATRIX4; + default: + break; + } + return ST_UNKNOWN; +} + +static StType int_type(int elem) +{ + switch(elem) { + case 1: + return ST_INT; + case 2: + return ST_INT2; + case 3: + return ST_INT3; + case 4: + return ST_INT4; + default: + break; + } + return ST_UNKNOWN; +} diff --git a/src/unistate.h b/src/unistate.h new file mode 100644 index 0000000..b7e66ad --- /dev/null +++ b/src/unistate.h @@ -0,0 +1,104 @@ +#ifndef UNISTATE_H_ +#define UNISTATE_H_ + +#include + +class ShaderProg; + +enum StType { + ST_UNKNOWN, + ST_INT, ST_INT2, ST_INT3, ST_INT4, + ST_FLOAT, ST_FLOAT2, ST_FLOAT3, ST_FLOAT4, + ST_MATRIX3, ST_MATRIX4 +}; + +int add_unistate(const char *name, StType type); +int get_unistate_index(const char *name); + +/** set the uniform state identified by \param sidx by copying + * a number of elements from \param val. If \param count is 0 + * then it's automatically set based on the type of this state item. + * @{ */ +void set_unistate(int sidx, const int *val, int count = 0); +void set_unistate(int sidx, const float *val, int count = 0); +/// @} + +/** get the uniform state identified by \param sidx by copying + * a number of elements into \param val. If \param count is 0 + * then it's automatically set based on the type of this state item. + * @{ */ +void get_unistate(int sidx, int *val, int count = 0); +void get_unistate(int sidx, float *val, int count = 0); +/// @} + +/// convenience versions of set_unistate @{ +void set_unistate(int sidx, int val); +void set_unistate(int sidx, float val); +void set_unistate(int sidx, const Vec2 &vec); +void set_unistate(int sidx, const Vec3 &vec); +void set_unistate(int sidx, const Vec4 &vec); +void set_unistate(int sidx, const Mat3 &mat); +void set_unistate(int sidx, const Mat4 &mat); +/// @} + +/** convenience functions for setting the uniform state by name. + * if the name cannot be found in the current set of uniform state + * items, a new one is created with a type derived from the variant + * of the function that was called (which might not be what you want). + * The index of the state item is returned. + * @{ */ +int set_unistate(const char *name, int *val, int count = 0); +int set_unistate(const char *name, float *val, int count = 0); +int set_unistate(const char *name, int val); +int set_unistate(const char *name, float val); +int set_unistate(const char *name, const Vec2 &vec); +int set_unistate(const char *name, const Vec3 &vec); +int set_unistate(const char *name, const Vec4 &vec); +int set_unistate(const char *name, const Mat3 &mat); +int set_unistate(const char *name, const Mat4 &mat); +/// @} + +/// convenience versions of get_unistate @{ +int get_unistate_int(int sidx); +float get_unistate_float(int sidx); +Vec2 get_unistate_vec2(int sidx); +Vec3 get_unistate_vec3(int sidx); +Vec4 get_unistate_vec4(int sidx); +Mat3 get_unistate_mat3(int sidx); +Mat4 get_unistate_mat4(int sidx); +/// @} + +/// convenience versions of get_unistate for getting the uniform state by name @{ +int get_unistate_int(const char *name); +float get_unistate_float(const char *name); +Vec2 get_unistate_vec2(const char *name); +Vec3 get_unistate_vec3(const char *name); +Vec4 get_unistate_vec4(const char *name); +Mat3 get_unistate_mat3(const char *name); +Mat4 get_unistate_mat4(const char *name); +/// @} + +/** Prepare for rendering by setting up all the state uniforms in the shader sdr. + * If sdr is null, then use the "current" shader as per ShaderProg::current + */ +void setup_unistate(const ShaderProg *sdr = 0); + +bool setup_unistate(int sidx, const ShaderProg *sdr, int loc); +bool setup_unistate(const char *name, const ShaderProg *sdr); + +// special functions for setting the rendering pipeline matrices +void set_world_matrix(const Mat4 &mat); +void set_view_matrix(const Mat4 &mat); +void set_projection_matrix(const Mat4 &mat); +void set_texture_matrix(const Mat4 &mat); + +Mat4 get_world_matrix(); +Mat4 get_view_matrix(); +Mat4 get_projection_matrix(); +Mat4 get_texture_matrix(); + +void setup_gl_matrices(); // this shouldn't be needed in the final code + +// TODO should do a matrix stack at some point ... + +#endif // UNISTATE_H_ -- 1.7.10.4