// -*- c++ -*-
#ifndef SHADER_H
#define SHADER_H
#include <stdio.h>
#include <string>
#include "pstring.h"
#include "util.h"
static uint pobject_in_use = 0;
class pShader {
public:
pShader(const char *path, const char *main_body_vs,
const char *main_body_fs = NULL)
{init(path,main_body_vs,main_body_fs);}
pShader()
{
validated = true;
pobject = 0;
}
# ifdef GL_VERTEX_SHADER
GLint uniform_location(const char *name)
{
const GLint rv = ptr_glGetUniformLocation(pobject,name);
if ( rv == -1 ) pError_Msg("Could not get uniform.");
return rv;
}
// Note: Location is "generic attribute index"
GLint attribute_location(const char *name)
{return glGetAttribLocation(pobject,name);}
GLint varying_location(const char *name)
{return ptr_glGetVaryingLocationNV(pobject, name);}
private:
GLuint shader_load
(GLenum shader_type, const char *source_path, const char *main_body)
{
const GLuint sobject = glCreateShader(shader_type);
pError_Check();
if ( !sobject )
{
// Could not create shader, perhaps feature not supported.
return 0;
}
FILE* const shader_h = fopen(source_path,"r");
if ( !shader_h )
{
fprintf(stderr,"Shader source file %s could not be open.\n",
source_path);
return 0;
}
std::string shader_text;
while ( !feof(shader_h) )
{
const int c = getc(shader_h);
if ( c == EOF ) break;
shader_text += c;
}
fclose(shader_h);
shader_text += "void main() {\n";
shader_text += main_body;
shader_text += "}\n";
const char *shader_text_lines = shader_text.c_str();
glShaderSource(sobject,1,&shader_text_lines,NULL);
glCompileShader(sobject);
int rv;
glGetShaderiv(sobject,GL_COMPILE_STATUS,&rv);
int info_log_length;
if ( !rv )
{
printf(" Compile status for %s: %d\n",main_body,rv);
glGetShaderiv(sobject,GL_INFO_LOG_LENGTH,&info_log_length);
char* const info_log = (char*) alloca(info_log_length+1);
glGetShaderInfoLog(sobject,info_log_length+1,NULL,info_log);
printf(" Info log:\n%s\n",info_log);
}
return sobject;
}
public:
uint init
(const char *source_pathp, const char *main_body_vs,
const char *main_body_fs = NULL)
{
validated = false;
source_path = source_pathp;
pobject = glCreateProgram();
if ( pobject == 0 ) return 0;
vs_object = shader_load(GL_VERTEX_SHADER,source_path,main_body_vs);
pError_Check();
if ( vs_object == 0 ) return 0;
glAttachShader(pobject,vs_object);
if ( main_body_fs )
{
fs_object = shader_load(GL_FRAGMENT_SHADER,source_path,main_body_fs);
if ( fs_object == 0 ) return 0;
glAttachShader(pobject,fs_object);
}
else
{
fs_object = 0;
}
pError_Check();
glLinkProgram(pobject);
pError_Check();
GLint link_status;
glGetProgramiv(pobject,GL_LINK_STATUS,&link_status);
if ( !link_status )
{
printf(" Link status for %s:%s %d\n",
source_path.s,main_body_vs, link_status);
GLint info_log_length;
glGetProgramiv(pobject,GL_INFO_LOG_LENGTH,&info_log_length);
char* const prog_info_log = (char*) alloca(info_log_length+1);
glGetProgramInfoLog(pobject,info_log_length+1,NULL,prog_info_log);
printf(" Program Info log:\n%s\n",prog_info_log);
}
return pobject;
}
void print_active_attrib()
{
if ( !pobject ) return;
int active_attributes, max_length;
pError_Check();
glGetProgramiv(pobject,GL_ACTIVE_ATTRIBUTES,&active_attributes);
pError_Check();
glGetProgramiv(pobject,GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,&max_length);
pError_Check();
char* const buffer = (char*) alloca(max_length);
for ( int i=0; i<active_attributes; i++ )
{
int size;
GLenum type;
glGetActiveAttrib(pobject,i,max_length,NULL,&size,&type,buffer);
pError_Check();
const int location = attribute_location(buffer);
printf("A %2d: SIZE %2d TYPE %6d %s\n",location,size,type,buffer);
}
}
void print_active_uniform()
{
if ( !pobject ) return;
int active_attributes, max_length;
glGetProgramiv(pobject,GL_ACTIVE_UNIFORMS,&active_attributes);
glGetProgramiv(pobject,GL_ACTIVE_UNIFORM_MAX_LENGTH,&max_length);
char* const buffer = (char*) alloca(max_length);
for ( int i=0; i<active_attributes; i++ )
{
int size;
GLenum type;
glGetActiveUniform(pobject,i,max_length,NULL,&size,&type,buffer);
printf("U %2d: SIZE %2d TYPE %6d %s\n",i,size,type,buffer);
}
}
void print_active_varying()
{
#ifndef GL_NV_transform_feedback
return;
#else
if ( !pobject ) return;
int active_attributes, max_length;
glGetProgramiv(pobject,GL_ACTIVE_VARYINGS_NV,&active_attributes);
glGetProgramiv(pobject,GL_ACTIVE_VARYING_MAX_LENGTH_NV,&max_length);
char* const buffer = (char*) alloca(max_length);
for ( int i=0; i<active_attributes; i++ )
{
int size;
GLenum type;
ptr_glGetActiveVaryingNV(pobject,i,max_length,NULL,&size,&type,buffer);
printf("V %2d: SIZE %2d TYPE %6d %s\n",i,size,type,buffer);
}
#endif
}
void validate_once()
{
if ( validated ) return;
validate();
}
void validate()
{
validated = true;
glValidateProgram(pobject);
int validate_status;
glGetProgramiv(pobject,GL_VALIDATE_STATUS,&validate_status);
printf(" Validate status for %s: %d\n",source_path.s,validate_status);
int info_log_length;
glGetProgramiv(pobject,GL_INFO_LOG_LENGTH,&info_log_length);
char* const prog_info_log = (char*) alloca(info_log_length+1);
glGetProgramInfoLog(pobject,info_log_length+1,NULL,prog_info_log);
printf(" Validation log:\n%s\n",prog_info_log);
}
bool use()
{
if ( pobject_in_use == pobject ) return false;
glUseProgram(pobject);
pError_Check();
pobject_in_use = pobject;
return true;
}
#else
// OpenGL Before Vertex Shaders
GLint uniform_location(const char *name) { return 0; }
GLint attribute_location(const char *name) { return 0; }
GLint varying_location(const char *name) { return 0; }
uint init(const char *source_pathp, const char *main_body)
{
validated = false;
source_path = source_pathp;
pobject = 0;
pobject_in_use = 0;
return pobject;
}
void print_active_attrib() { }
void print_active_uniform() { }
void print_active_varying() { }
void validate_once() { }
void validate() { };
bool use() { return false; }
#endif
pString source_path;
GLuint pobject;
GLuint vs_object, fs_object;
bool validated;
};
#endif