Last active
November 19, 2017 00:43
-
-
Save jtsiomb/504a1021f922dce94ce155c2438f4a75 to your computer and use it in GitHub Desktop.
OpenGL global uniform state tracking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef UNISTATE_H_ | |
#define UNISTATE_H_ | |
#include "vmath/vmath.h" | |
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 Vector2 &vec); | |
void set_unistate(int sidx, const Vector3 &vec); | |
void set_unistate(int sidx, const Vector4 &vec); | |
void set_unistate(int sidx, const Matrix3x3 &mat); | |
void set_unistate(int sidx, const Matrix4x4 &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 Vector2 &vec); | |
int set_unistate(const char *name, const Vector3 &vec); | |
int set_unistate(const char *name, const Vector4 &vec); | |
int set_unistate(const char *name, const Matrix3x3 &mat); | |
int set_unistate(const char *name, const Matrix4x4 &mat); | |
/// @} | |
/// convenience versions of get_unistate @{ | |
int get_unistate_int(int sidx); | |
float get_unistate_float(int sidx); | |
Vector2 get_unistate_vec2(int sidx); | |
Vector3 get_unistate_vec3(int sidx); | |
Vector4 get_unistate_vec4(int sidx); | |
Matrix3x3 get_unistate_mat3(int sidx); | |
Matrix4x4 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); | |
Vector2 get_unistate_vec2(const char *name); | |
Vector3 get_unistate_vec3(const char *name); | |
Vector4 get_unistate_vec4(const char *name); | |
Matrix3x3 get_unistate_mat3(const char *name); | |
Matrix4x4 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 Matrix4x4 &mat); | |
void set_view_matrix(const Matrix4x4 &mat); | |
void set_projection_matrix(const Matrix4x4 &mat); | |
void set_texture_matrix(const Matrix4x4 &mat); | |
Matrix4x4 get_world_matrix(); | |
Matrix4x4 get_view_matrix(); | |
Matrix4x4 get_projection_matrix(); | |
Matrix4x4 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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SHADER_H_ | |
#define SHADER_H_ | |
#include <vector> | |
#include "vmath/vmath.h" | |
#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; | |
public: | |
Shader(); | |
~Shader(); | |
unsigned int get_id() const; | |
unsigned int get_type() const; | |
void set_name(const char *name); | |
const char *get_name() const; | |
bool create(const char *src, unsigned int type); | |
void destroy(); | |
bool load(const char *fname, unsigned int type); | |
}; | |
#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<Shader*> 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<StateLocCache> 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 Vector2 &v) const; | |
bool set_uniform(int loc, const Vector3 &v) const; | |
bool set_uniform(int loc, const Vector4 &v) const; | |
bool set_uniform(int loc, const Matrix3x3 &m) const; | |
bool set_uniform(int loc, const Matrix4x4 &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 Vector2 &v) const; | |
bool set_uniform(const char *name, const Vector3 &v) const; | |
bool set_uniform(const char *name, const Vector4 &v) const; | |
bool set_uniform(const char *name, const Matrix3x3 &m) const; | |
bool set_uniform(const char *name, const Matrix4x4 &m) const; | |
friend void setup_unistate(const ShaderProg*); | |
}; | |
class ShaderSet : public DataSet<Shader*> { | |
private: | |
unsigned int type; | |
public: | |
ShaderSet(unsigned int type); | |
}; | |
#endif // SHADER_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <map> | |
#include <vector> | |
#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<StateItem> state; | |
std::map<std::string, int> 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<std::string, int>::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 Vector2 &vec) | |
{ | |
set_unistate(sidx, &vec.x, 2); | |
} | |
void set_unistate(int sidx, const Vector3 &vec) | |
{ | |
set_unistate(sidx, &vec.x, 3); | |
} | |
void set_unistate(int sidx, const Vector4 &vec) | |
{ | |
set_unistate(sidx, &vec.x, 4); | |
} | |
void set_unistate(int sidx, const Matrix3x3 &mat) | |
{ | |
set_unistate(sidx, mat[0], 9); | |
state[sidx].transpose = 1; | |
} | |
void set_unistate(int sidx, const Matrix4x4 &mat) | |
{ | |
set_unistate(sidx, mat[0], 16); | |
state[sidx].transpose = 1; | |
} | |
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 Vector2 &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 Vector3 &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 Vector4 &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 Matrix3x3 &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 Matrix4x4 &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; | |
} | |
Vector2 get_unistate_vec2(int sidx) | |
{ | |
float val[2] = {0.0f, 0.0f}; | |
get_unistate(sidx, val, 2); | |
return Vector2(val[0], val[1]); | |
} | |
Vector3 get_unistate_vec3(int sidx) | |
{ | |
float val[3] = {0.0f, 0.0f, 0.0f}; | |
get_unistate(sidx, val, 3); | |
return Vector3(val[0], val[1], val[2]); | |
} | |
Vector4 get_unistate_vec4(int sidx) | |
{ | |
float val[4] = {0.0f, 0.0f, 0.0f}; | |
get_unistate(sidx, val, 4); | |
return Vector4(val[0], val[1], val[2], val[3]); | |
} | |
Matrix3x3 get_unistate_mat3(int sidx) | |
{ | |
Matrix3x3 res; | |
get_unistate(sidx, res.m[0], 9); | |
return res; | |
} | |
Matrix4x4 get_unistate_mat4(int sidx) | |
{ | |
Matrix4x4 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); | |
} | |
Vector2 get_unistate_vec2(const char *name) | |
{ | |
int sidx = get_unistate_index(name); | |
if(sidx == -1) { | |
return Vector2(); | |
} | |
return get_unistate_vec2(sidx); | |
} | |
Vector3 get_unistate_vec3(const char *name) | |
{ | |
int sidx = get_unistate_index(name); | |
if(sidx == -1) { | |
return Vector3(); | |
} | |
return get_unistate_vec3(sidx); | |
} | |
Vector4 get_unistate_vec4(const char *name) | |
{ | |
int sidx = get_unistate_index(name); | |
if(sidx == -1) { | |
return Vector4(); | |
} | |
return get_unistate_vec4(sidx); | |
} | |
Matrix3x3 get_unistate_mat3(const char *name) | |
{ | |
int sidx = get_unistate_index(name); | |
if(sidx == -1) { | |
return Matrix3x3(); | |
} | |
return get_unistate_mat3(sidx); | |
} | |
Matrix4x4 get_unistate_mat4(const char *name) | |
{ | |
int sidx = get_unistate_index(name); | |
if(sidx == -1) { | |
return Matrix4x4(); | |
} | |
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; | |
} | |
CHECKGLERR; | |
glUseProgram(sdr->get_id()); | |
CHECKGLERR; | |
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; | |
} | |
CHECKGLERR; | |
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 Matrix4x4 &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, Matrix3x3(mat)); | |
set_unistate(sidx_transp, mat[0]); // by using the float* variant, we unset the transpose flag | |
} | |
void set_view_matrix(const Matrix4x4 &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, Matrix3x3(mat)); | |
set_unistate(sidx_transp, mat[0]); // by using the float* variant, we unset the transpose flag | |
} | |
void set_projection_matrix(const Matrix4x4 &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 Matrix4x4 &mat) | |
{ | |
static int sidx = -1; | |
if(sidx == -1) { | |
sidx = add_unistate("st_tex_matrix", ST_MATRIX4); | |
} | |
set_unistate(sidx, mat); | |
} | |
Matrix4x4 get_world_matrix() | |
{ | |
static int sidx = -1; | |
if(sidx == -1) { | |
if((sidx = get_unistate_index("st_world_matrix")) == -1) { | |
return Matrix4x4(); | |
} | |
} | |
return get_unistate_mat4(sidx); | |
} | |
Matrix4x4 get_view_matrix() | |
{ | |
static int sidx = -1; | |
if(sidx == -1) { | |
if((sidx = get_unistate_index("st_view_matrix")) == -1) { | |
return Matrix4x4(); | |
} | |
} | |
return get_unistate_mat4(sidx); | |
} | |
Matrix4x4 get_projection_matrix() | |
{ | |
static int sidx = -1; | |
if(sidx == -1) { | |
if((sidx = get_unistate_index("st_proj_matrix")) == -1) { | |
return Matrix4x4(); | |
} | |
} | |
return get_unistate_mat4(sidx); | |
} | |
Matrix4x4 get_texture_matrix() | |
{ | |
static int sidx = -1; | |
if(sidx == -1) { | |
if((sidx = get_unistate_index("st_tex_matrix")) == -1) { | |
return Matrix4x4(); | |
} | |
} | |
return get_unistate_mat4(sidx); | |
} | |
void setup_gl_matrices() | |
{ | |
#ifdef USE_OLDGL | |
Matrix4x4 modelview = get_view_matrix() * get_world_matrix(); | |
Matrix4x4 proj = get_projection_matrix(); | |
Matrix4x4 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 "<unknown>"; | |
} | |
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <errno.h> | |
#include "opengl.h" | |
#include "shader.h" | |
#include "logger.h" | |
#include "unistate.h" | |
#include "mesh.h" | |
#include "assman.h" | |
#ifdef _WIN32 | |
#include <malloc.h> | |
#else | |
#include <alloca.h> | |
#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 = 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; | |
} | |
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(!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 [] name; | |
name = 0; | |
} | |
bool Shader::load(const char *fname, unsigned int type) | |
{ | |
ass_file *fp; | |
if(!(fp = ass_fopen(fname, "rb"))) { | |
error_log("failed to open %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); | |
return false; | |
} | |
ass_fseek(fp, 0, SEEK_END); | |
long sz = ass_ftell(fp); | |
ass_fseek(fp, 0, SEEK_SET); | |
char *src = (char*)alloca(sz + 1); | |
if(ass_fread(src, 1, sz, fp) < (size_t)sz) { | |
error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); | |
ass_fclose(fp); | |
return false; | |
} | |
src[sz] = 0; | |
ass_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); | |
CHECKGLERR; | |
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 | |
{ | |
CHECKGLERR; | |
if(must_link) { | |
if(!link()) { | |
return; | |
} | |
} | |
CHECKGLERR; | |
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 Vector2 &v) const | |
{ | |
bind(); | |
if(loc >= 0) { | |
glUniform2f(loc, v.x, v.y); | |
return true; | |
} | |
return false; | |
} | |
bool ShaderProg::set_uniform(int loc, const Vector3 &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 Vector4 &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 Matrix3x3 &m) const | |
{ | |
bind(); | |
if(loc >= 0) { | |
glUniformMatrix3fv(loc, 1, GL_TRUE, m[0]); | |
return true; | |
} | |
return false; | |
} | |
bool ShaderProg::set_uniform(int loc, const Matrix4x4 &m) const | |
{ | |
bind(); | |
if(loc >= 0) { | |
glUniformMatrix4fv(loc, 1, GL_TRUE, 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 Vector2 &v) const | |
{ | |
return set_uniform(get_uniform_location(name), v); | |
} | |
bool ShaderProg::set_uniform(const char *name, const Vector3 &v) const | |
{ | |
return set_uniform(get_uniform_location(name), v); | |
} | |
bool ShaderProg::set_uniform(const char *name, const Vector4 &v) const | |
{ | |
return set_uniform(get_uniform_location(name), v); | |
} | |
bool ShaderProg::set_uniform(const char *name, const Matrix3x3 &m) const | |
{ | |
return set_uniform(get_uniform_location(name), m); | |
} | |
bool ShaderProg::set_uniform(const char *name, const Matrix4x4 &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; i<num_uni; i++) { | |
GLint sz; | |
GLenum type; | |
glGetActiveUniform(prog, i, sizeof name - 1, 0, &sz, &type, name); | |
if(strstr(name, "st_") == name) { | |
StateLocCache s; | |
s.sidx = add_unistate(name, unist_type(type)); | |
s.loc = glGetUniformLocation(prog, name); | |
stloc_cache.push_back(s); | |
} | |
} | |
} | |
void ShaderProg::setup_state_uniforms() const | |
{ | |
#ifdef USE_OLDGL | |
setup_gl_matrices(); | |
#endif | |
for(size_t i=0; i<stloc_cache.size(); i++) { | |
setup_unistate(stloc_cache[i].sidx, this, stloc_cache[i].loc); | |
CHECKGLERR; | |
} | |
} | |
// ---- ShaderSet ---- | |
static Shader *load_shader(const char *fname, unsigned int type) | |
{ | |
Shader *sdr = new Shader; | |
if(!sdr->load(fname, type)) { | |
delete sdr; | |
return 0; | |
} | |
return sdr; | |
} | |
static Shader *load_vertex_shader(const char *fname) | |
{ | |
return load_shader(fname, GL_VERTEX_SHADER); | |
} | |
static Shader *load_pixel_shader(const char *fname) | |
{ | |
return load_shader(fname, GL_FRAGMENT_SHADER); | |
} | |
#ifdef HAVE_GEOMETRY_SHADER | |
static Shader *load_geom_shader(const char *fname) | |
{ | |
return load_shader(fname, GL_GEOMETRY_SHADER); | |
} | |
#endif | |
#ifdef HAVE_TESSELATION_SHADER | |
static Shader *load_tc_shader(const char *fname) | |
{ | |
return load_shader(fname, GL_TESS_CONTROL_SHADER); | |
} | |
static Shader *load_te_shader(const char *fname) | |
{ | |
return load_shader(fname, GL_TESS_EVALUATION_SHADER); | |
} | |
#endif | |
static void destroy_shader(Shader *sdr) | |
{ | |
delete sdr; | |
} | |
ShaderSet::ShaderSet(unsigned int type) | |
: DataSet<Shader*>(0, 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; i<num_attr; i++) { | |
GLint sz; | |
GLenum type; | |
glGetActiveAttrib(prog->get_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 "<unknown>"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment