Skip to content

Instantly share code, notes, and snippets.

@szastupov
Created April 23, 2011 13:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save szastupov/938592 to your computer and use it in GitHub Desktop.
Save szastupov/938592 to your computer and use it in GitHub Desktop.
#include "material.h"
#include "opengl.h"
uint32_t VertexAttrib::convertType(type_t type)
{
switch (type) {
case FLOAT:
return GL_FLOAT;
case SHORT:
return GL_SHORT;
case USHORT:
return GL_UNSIGNED_SHORT;
case BYTE:
return GL_BYTE;
case UBYTE:
return GL_UNSIGNED_BYTE;
}
ASSERT(!"should not be reached");
return 0;
}
void VertexAttrib::bind() const
{
m_vb->bind();
glVertexAttribPointer(m_loc,
m_components,
m_type,
GL_FALSE,
m_stride,
(GLvoid*)m_offset);
glEnableVertexAttribArray(m_loc);
}
void VertexAttrib::unbind() const
{
glDisableVertexAttribArray(m_loc);
}
Material::Material(ShaderProgramPtr prog)
: m_program(prog)
, m_MatrixModelView(prog->getUniformID("g_ModelView", Uniform::MATRIX4))
, m_MatrixProjection(prog->getUniformID("g_Projection", Uniform::MATRIX4))
, m_MatrixNormal(prog->getUniformID("g_Normal", Uniform::MATRIX3))
, m_textureUnits(0)
, m_blending(REPLACE)
, m_depthWrite(true)
{}
UniformTexture* Material::addUniformTexture(const char *name)
{
int loc = m_program->getUniformID(name, Uniform::TEXTURE);
if (loc == -1)
FAIL("uniform %s not found\n", name);
int textureUnit = m_textureUnits++;
UniformTexture *uv = new UniformTexture(loc, textureUnit);
m_uniforms.push_back(uv);
return uv;
}
Uniform* Material::findUniform(int id)
{
for (size_t i = 0; i < m_uniforms.size(); i++)
if (m_uniforms[i].getID() == id)
return &m_uniforms[i];
return NULL;
}
VBOPtr Material::addAttrib(const char *name,
uint32_t components,
VertexAttrib::type_t type,
VBOPtr va,
uint32_t stride, uint32_t offset)
{
ASSERT(va->isArrayBuffer());
int loc = m_program->getAttribID(name, components);
m_attributes.push_back(VertexAttrib(loc, components, type, stride, offset, va));
return va;
}
void Material::loadBlending() const
{
switch (m_blending) {
case REPLACE:
glBlendFunc(GL_ONE, GL_ZERO);
break;
case ADD:
glBlendFunc(GL_ONE, GL_ONE);
break;
case ALPHA:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case ALPHA_ADD:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
}
}
void Material::loadMatrices(const Matrix4f &model, const Matrix4f &projection) const
{
if (m_MatrixModelView.getID() != -1)
m_MatrixModelView.loadValue(model);
if (m_MatrixProjection.getID() != -1)
m_MatrixProjection.loadValue(projection);
if (m_MatrixNormal.getID() != -1) {
Matrix3f model3x3(model);
m_MatrixNormal.loadValue(model3x3.inverseTransposed());
}
}
void Material::loadUniforms() const
{
for (size_t i = 0; i < m_uniforms.size(); i++)
m_uniforms[i].load();
}
void Material::loadAttributes() const
{
for (size_t i = 0; i < m_attributes.size(); i++)
m_attributes[i].bind();
}
void Material::unloadAttributes() const
{
for (size_t i = 0; i < m_attributes.size(); i++)
m_attributes[i].unbind();
}
Material* Material::clone() const
{
Material *mclone = new Material(m_program);
mclone->m_uniforms.reserve(m_uniforms.size());
for (size_t i = 0; i < m_uniforms.size(); i++)
mclone->m_uniforms.push_back(m_uniforms[i].clone());
mclone->m_attributes = m_attributes;
mclone->m_textureUnits = m_textureUnits;
mclone->m_blending = m_blending;
return mclone;
}
#ifndef MATERIAL_H
#define MATERIAL_H
#include <boost/ptr_container/ptr_vector.hpp>
#include "shader.h"
#include "buffers.h"
#include "sp.h"
struct VertexAttrib {
public:
typedef enum {
FLOAT, SHORT, USHORT, BYTE, UBYTE
} type_t;
VertexAttrib(uint32_t loc,
uint32_t components,
type_t type,
uint32_t stride,
uint32_t offset,
VBOPtr va)
: m_loc(loc)
, m_components(components)
, m_type(convertType(type))
, m_stride(stride)
, m_offset(offset)
, m_vb(va)
{
}
void bind() const;
void unbind() const;
private:
uint32_t m_loc;
uint32_t m_components;
uint32_t m_type;
uint32_t m_stride;
uint32_t m_offset;
VBOPtr m_vb;
static uint32_t convertType(type_t);
};
class Material : public RefCounted<Material> {
public:
typedef enum {
REPLACE, ADD, ALPHA, ALPHA_ADD
} blending_t;
explicit Material(ShaderProgramPtr prog);
UniformTexture* addUniformTexture(const char *name);
template <class T>
UniformValue<T>* addUniformValue(const char *name)
{
int type = UniformTypeTraits<T>::type;
int id = m_program->getUniformID(name, type);
if (id == -1)
FAIL("uniform %s not found\n", name);
UniformValue<T> *uv = new UniformValue<T>(id);
m_uniforms.push_back(uv);
return uv;
}
Uniform* findUniform(int id);
template <class T>
UniformValue<T>* getUniformValue(const char *name)
{
int type = UniformTypeTraits<T>::type;
int id = m_program->getUniformID(name, type);
if (id == -1)
FAIL("uniform %s not found\n", name);
Uniform *uf = findUniform(id);
if (uf)
return static_cast<UniformValue<T>*>(uf);
else {
UniformValue<T> *uv = new UniformValue<T>(id);
m_uniforms.push_back(uv);
return uv;
}
}
VBOPtr addAttrib(const char *name,
uint32_t components,
VertexAttrib::type_t type,
VBOPtr va,
uint32_t stride, uint32_t offset);
VBOPtr addAttrib(const char *name,
uint32_t components,
VertexAttrib::type_t type,
VBOPtr va)
{
return addAttrib(name, components, type, va, 0, 0);
}
const ShaderProgram* getProgram() const
{
return m_program.get();
}
void loadUniforms() const;
void loadAttributes() const;
void unloadAttributes() const;
void loadMatrices(const Matrix4f &model, const Matrix4f &projection) const;
void loadBlending() const;
void setBlending(blending_t b)
{
m_blending = b;
}
blending_t getBlending() const
{
return m_blending;
}
void setDepthWrite(bool enable)
{
m_depthWrite = enable;
}
bool getDepthWrite() const
{
return m_depthWrite;
}
Material* clone() const;
private:
ShaderProgramPtr m_program;
boost::ptr_vector<Uniform> m_uniforms;
std::vector<VertexAttrib> m_attributes;
Uniform m_MatrixModelView;
Uniform m_MatrixProjection;
Uniform m_MatrixNormal;
int m_textureUnits;
blending_t m_blending;
bool m_depthWrite;
};
typedef boost::intrusive_ptr<Material> MaterialPtr;
#include <string>
#include <cstring>
#include <algorithm>
#include "opengl.h"
#include "shader.h"
Shader::Shader(GLenum type, const char *origin, const char *source)
: m_type(type)
, m_shader(0)
{
m_shader = glCreateShader(m_type);
glShaderSource(m_shader, 1, &source, NULL);
glCompileShader(m_shader);
GLint shader_ok;
glGetShaderiv(m_shader, GL_COMPILE_STATUS, &shader_ok);
if (!shader_ok) {
GLint logLen = 0;
// Fuck you Qualcomm, your crappy drivers is PITA.
// Why the fuck GL_INFO_LOG_LENGTH is always zero?
#ifdef ANDROID
logLen = 1024;
#else
glGetShaderiv(m_shader, GL_INFO_LOG_LENGTH, &logLen);
#endif
char *lbuf = new char[logLen];
glGetShaderInfoLog(m_shader, logLen, NULL, lbuf);
FAIL("Failed to compile shader %s: %s", origin, lbuf);
// We are dying anyway, so there is no point to free memory
}
}
Shader::~Shader()
{
glDeleteShader(m_shader);
}
GLuint Shader::get() const
{
return m_shader;
}
ShaderProgram::ShaderProgram()
: m_program(0)
{
m_program = glCreateProgram();
}
ShaderProgram::~ShaderProgram()
{
glDeleteProgram(m_program);
}
void ShaderProgram::addShader(const Shader &shader)
{
glAttachShader(m_program, shader.get());
}
static int convertUniformType(GLenum type)
{
#define CASE(g, v) case g: return Uniform::v
switch (type) {
CASE(GL_FLOAT_MAT4, MATRIX4);
CASE(GL_FLOAT_MAT3, MATRIX3);
CASE(GL_FLOAT_VEC4, VEC4);
CASE(GL_FLOAT_VEC3, VEC3);
CASE(GL_INT, INT);
CASE(GL_FLOAT, FLOAT);
CASE(GL_SAMPLER_2D, TEXTURE);
default:
ASSERT(0);
}
#undef CASE
return -1;
}
static uint32_t getComponentsCount(uint32_t type, const char *name)
{
switch (type) {
case GL_FLOAT:
return 1;
break;
case GL_FLOAT_VEC2:
return 2;
break;
case GL_FLOAT_VEC3:
return 3;
break;
case GL_FLOAT_VEC4:
return 4;
break;
default:
FAIL("don't know how to handle attribute type %d for %s", type, name);
break;
}
}
template <class T>
static bool namecmp(const T &v1, const T &v2)
{
return strcmp(v1.name, v2.name) < 0;
}
void ShaderProgram::link()
{
glLinkProgram(m_program);
GLint program_ok;
glGetProgramiv(m_program, GL_LINK_STATUS, &program_ok);
if (!program_ok) {
GLint logLen = 0;
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &logLen);
char *lbuf = new char[logLen];
glGetProgramInfoLog(m_program, logLen, NULL, lbuf);
FAIL("Failed to compile program: %s", lbuf);
}
int nuf = 0;
glGetProgramiv(m_program, GL_ACTIVE_UNIFORMS, &nuf);
m_uniforms.resize(nuf);
for (int i = 0; i < nuf; i++) {
int len, size;
GLenum type;
UniformLocation &ul = m_uniforms[i];
glGetActiveUniform(m_program, i, sizeof(ul.name), &len, &size, &type, ul.name);
ul.location = glGetUniformLocation(m_program, ul.name);
ul.type = convertUniformType(type);
}
std::sort(m_uniforms.begin(), m_uniforms.end(), namecmp<UniformLocation>);
int nattr = 0;
glGetProgramiv(m_program, GL_ACTIVE_ATTRIBUTES, &nattr);
m_attributes.resize(nattr);
for (int i = 0; i < nattr; i++) {
int len, size;
GLenum type;
AttributeLocation &al = m_attributes[i];
glGetActiveAttrib(m_program, i, sizeof(al.name), &len, &size, &type, al.name);
al.location = glGetAttribLocation(m_program, al.name);
al.components = getComponentsCount(type, al.name);
}
std::sort(m_attributes.begin(), m_attributes.end(), namecmp<AttributeLocation>);
}
void ShaderProgram::use() const
{
glUseProgram(m_program);
}
template <class T>
const T* binsearch(const std::vector<T> &vec, const char *key)
{
int from = 0, to = vec.size();
while (to-from > 0) {
size_t n = (to-from)/2;
size_t in = from+n;
const T *val = &vec[in];
int cmp = strcmp(key, val->name);
if (cmp == 0)
return val;
if (cmp < 0)
to = in;
else
from = in+1;
}
return NULL;
}
GLint ShaderProgram::getAttribID(const char *name, uint32_t components)
{
const AttributeLocation *al = binsearch(m_attributes, name);
if (!al)
FAIL("attribute %s not found\n", name);
if (al->components < components)
FAIL("shader attribute %s declares %d components but you are trying to bind %d components",
name, al->components, components);
return al->location;
}
int ShaderProgram::getUniformID(const char *name, int type) const
{
const UniformLocation *ul = binsearch(m_uniforms, name);
if (!ul)
return -1;
if (ul->type != type)
FAIL("uniform type missmatch %s %d != %d\n", name, ul->type, type);
return ul->location;
}
#ifndef SHADER_H
#define SHADER_H
#include <vector>
#include <stdint.h>
#include "uniforms.h"
#include "sp.h"
class Shader : public RefCounted<Shader> {
unsigned m_type;
unsigned m_shader;
public:
Shader(unsigned type, const char *origin, const char *source);
~Shader();
unsigned getType() const
{
return m_type;
}
unsigned get() const;
};
typedef boost::intrusive_ptr<Shader> ShaderPtr;
struct UniformLocation {
char name[64];
int location;
int type;
};
struct AttributeLocation {
char name[64];
uint32_t location;
uint32_t components;
};
class ShaderProgram : public RefCounted<ShaderProgram> {
public:
ShaderProgram();
~ShaderProgram();
void addShader(const Shader &shader);
void link();
void use() const;
int getAttribID(const char *name, uint32_t components);
int getUniformID(const char *name, int type) const;
const std::vector<UniformLocation>& getUniforms()
{
return m_uniforms;
}
private:
unsigned m_program;
std::vector<UniformLocation> m_uniforms;
std::vector<AttributeLocation> m_attributes;
};
typedef boost::intrusive_ptr<ShaderProgram> ShaderProgramPtr;
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment