yabba/shader.cpp
#include "shader.hpp"
#include "io.hpp"
#include <dirent.h>
Shader::Shader(GLuint s, GLenum t): shader(s), type(t) {
}
Shader* Shader::getInst(const char* buf, size_t len) {
const char* pos = strstr(buf, "///shaderType:");
if (!pos) {
LOG("no shaderType comment found");
return NULL;
}
pos += sizeof("///shaderType:")-1;
GLenum type;
if (!strncmp(pos, "vertex", sizeof("vertex")-1)) {
type = GL_VERTEX_SHADER;
} else if (!strncmp(pos, "fragment", sizeof("fragment")-1)) {
type = GL_FRAGMENT_SHADER;
} else if (!strncmp(pos, "geometry", sizeof("geometry")-1)) {
type = GL_GEOMETRY_SHADER;
} else {
return NULL;
}
if (type == GL_FRAGMENT_SHADER) {
unsigned outnum = 0;
pos = buf;
while ((pos = strstr(pos, "\nout ")) != NULL) {
outnum++;
pos++;
}
if (outnum > 1) {
LOG("glBindFragDataLocation would be needed for %u fragment outputs", outnum);
return NULL;
}
}
// TODO: better error detection
GLuint s = glCreateShader(type);
glShaderSource(s, 1, &buf, (int*)&len);
glCompileShader(s);
GLint status;
glGetShaderiv(s, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
static char buffer[512];
glGetShaderInfoLog(s, sizeof(buffer), NULL, buffer);
LOG("%s", buffer);
glDeleteShader(s);
return NULL;
}
return s? new Shader(s, type): NULL;
}
Shader* Shader::getInst(const char* fn) {
char* src;
size_t len;
if (!file_read(fn, src, len)) {
return NULL;
}
Shader* rv = getInst(src, len);
free(src);
return rv;
}
Shader::~Shader() {
glDeleteShader(shader);
}
Program* Program::getInst() {
GLuint p = glCreateProgram();
return new Program(p);
}
Program* Program::getInst(const char* dir) {
DIR* dirp = opendir(dir);
if (!dirp) {
LOG_ERRNO("opendir(%s)", dir);
return NULL;
}
Program* rv = getInst();
if (!rv) {
closedir(dirp);
return NULL;
}
char fn[PATH_MAX+1];
strcpy(fn, dir);
char* fnp = fn+strlen(fn);
*fnp = '/';
fnp++;
struct dirent* dp;
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_type != DT_REG) continue;
if (!strstr(dp->d_name, ".glsl")) continue;
strcpy(fnp, dp->d_name);
Shader* shader = Shader::getInst(fn);
if (!shader) {
delete rv;
rv = NULL;
break;
}
if (!rv->attach(shader)) {
delete shader;
delete rv;
rv = NULL;
break;
}
}
closedir(dirp);
return rv;
}
Program::~Program() {
glDeleteProgram(program);
for (std::vector<Shader*>::iterator it=shaders.begin(); it != shaders.end(); ++it) {
delete *it;
}
}
bool Program::attach(Shader* shader) {
glAttachShader(program, *shader);
shaders.push_back(shader);
return true;
}