rts_glShaderProgram.h 7.62 KB
#ifndef RTS_GLSHADERPROGRAM_H
#define RTS_GLSHADERPROGRAM_H

/*********************************************************
//create a shader program
	rts_glShaderProgram myProgram;
//initialize
	myProgram.Init();
//Attach shaders
	myProgram.AttachShader(GL_FRAGMENT_SHADER, "filename.glsl");
//Compile and link
	myProgram.Compile();
	myProgram.Link();
	myProgram.PrintLog();
//attach uniform variables
	myProgram.AttachTextureMap("texture", texture);
	myProgram.AttachGlobalUniform("light_intensity", &intensity);

//use the program
	myProgram.BeginProgram();
	//render
	myProgram.EndProgram();
**********************************************************/


#include "rts_glShaderObject.h"
#include "rts_glShaderUniform.h"
#include "rts_glTextureMap.h"
#include <algorithm>

using namespace std;

class rts_glShaderProgram
{
private:
	void get_uniforms()
	{
		GLint num_uniforms;
		glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &num_uniforms);		//get the number of uniform variables
		GLint max_name_length;
		glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_length);	//get the maximum uniform name length
		GLchar* name_buffer = new GLchar[max_name_length];			//create a buffer to store the name
		GLsizei length;						//I'm not using these yet
		GLint size;
		GLenum type;						//variable's data type
		GLint location;						//GPU location of the variable
		for(int i=0; i<num_uniforms; i++)		//create an rts_glShaderUniform structure for each variable
		{
			glGetActiveUniform(id, i, max_name_length, &length, &size, &type, name_buffer);	//get the uniform information
			location = glGetUniformLocation(id, name_buffer);		//get the GPU location of the variable
			//create the rts_glShaderUniform structure
			rts_glShaderUniform current;
			current.location = location;
			current.name = name_buffer;
			current.type = type;
			current.p_value = NULL;


			uniform_list.push_back(current);
		}

	}
	int get_index(const char* name)
	{
		unsigned int size = uniform_list.size();
		for(unsigned int i=0; i<size; i++)
		{
			if(uniform_list[i].name == name)
				return i;
		}
		return -1;
	}
	string log;
public:
	GLuint id;
	bool linked;
	vector<rts_glShaderObject> shader_list;	//list of opengl shaders
	vector<rts_glShaderUniform> uniform_list;	//list of active uniform variables
	vector<rts_glTextureMap> texture_list;		//list of texture maps

	rts_glShaderProgram()
	{
		linked = false;
		id = 0;
	}
	void AttachShader(rts_glShaderObject shader)
	{
		if(id == 0)
		{
			Init();
		}
		if(shader.id == 0)	//if the shader is invalid
		{
			log = "Shader is invalid";
			return;
		}

		//attach the shader to the program
		glAttachShader(id, shader.id);			//attach the shader to the program in OpenGL
		CHECK_OPENGL_ERROR
		shader_list.push_back(shader);			//push the shader onto our list for later access
	}
	//type = GL_FRAGMENT_SHADER or GL_VERTEX_SHADER
	void AttachShader(GLenum type, const char* filename)
	{
		rts_glShaderObject shader(type, filename);
		AttachShader(shader);
	}
	void AttachShader(GLenum type, rtsSourceCode source)
	{
		rts_glShaderObject shader(type, source);
		AttachShader(shader);
	}
	void PrintLog()
	{
		cout<<log;

		if(log.size() != 0) cout<<endl;
	}
	void Compile()
	{
		if(shader_list.size() == 0)
		{
			log = "No shaders to compile";
			return;
		}

		vector<rts_glShaderObject>::iterator iter;
		for(iter = shader_list.begin(); iter != shader_list.end(); iter++)
		{
			(*iter).Compile();
			//(*iter).PrintLog();
		}
	}
	void Link()
	{
		glLinkProgram(id);				//link the current shader program
		GLint link_status;				//test to see if the link went alright
		glGetProgramiv(id, GL_LINK_STATUS, &link_status);
		if(link_status != GL_TRUE)
		{
			linked = false;
		}
		else
			linked = true;

		GLsizei length;
		GLchar buffer[1000];
		glGetProgramInfoLog(id, 1000, &length, buffer);
		log = buffer;

		get_uniforms();			//create the list of active uniform variables
	}
	void BeginProgram()
	{
		CHECK_OPENGL_ERROR
		if(id == 0)				//if the program is invalid, return
		{
			log = "Invalid program, cannot use.";
			return;
		}
		if(!linked)
		{
			cout<<"Shader Program used without being linked."<<endl;
			//exit(1);
		}

		//set up all of the texture maps
		int num_textures = texture_list.size();

		for(int t=0; t<num_textures; t++)
		{
			glActiveTexture(GL_TEXTURE0 + t);
			CHECK_OPENGL_ERROR
			//glEnable(texture_list[t].texture_type);
			//CHECK_OPENGL_ERROR
			glBindTexture(texture_list[t].texture_type, texture_list[t].name);
			CHECK_OPENGL_ERROR
		}

		glUseProgram(id);
		CHECK_OPENGL_ERROR
	}
	void EndProgram()
	{
		CHECK_OPENGL_ERROR
		//return standard functionality
		int num_textures = texture_list.size();

		//disable all texture units
		for(int t=0; t<num_textures; t++)
		{
			glActiveTexture(GL_TEXTURE0 + t);
			glDisable(texture_list[t].texture_type);
			CHECK_OPENGL_ERROR
		}
		//make sure that the single default texture unit is active
		if(num_textures > 0)
			glActiveTexture(GL_TEXTURE0);
		CHECK_OPENGL_ERROR

		//return to OpenGL default shading
		glUseProgram(0);
		CHECK_OPENGL_ERROR
	}
	void PrintUniforms()
	{
		cout<<"Shader Uniforms: "<<endl;
		unsigned int i;
		for(i=0; i<uniform_list.size(); i++)
		{
			cout<<i<<":  "<<uniform_list[i].name<<"          "<<uniform_list[i].location<<endl;
		}
	}
	void AttachGlobalUniform(unsigned int index, void* param)		//attaches a global variable to the indexed uniform parameter
	{
		uniform_list[index].p_value = param;
	}
	void AttachGlobalUniform(const char* name, void* param)
	{
		//find the index of the shader
		int index = get_index(name);
		if(index != -1)
			AttachGlobalUniform(index, param);
		else
		{
			string strError = "Error finding uniform variable: ";
			strError += name;
			cout<<strError<<endl;
		}
	}
	void AttachTextureMap(unsigned int index, rts_glTextureMap texture)	//attaches a texture map to the program
	{
		//if there is not a texture map assigned to the variable
		if(uniform_list[index].p_value == NULL)
		{
			uniform_list[index].p_value = new unsigned int[1];
			((unsigned int*)uniform_list[index].p_value)[0] = texture_list.size();		//set the parameter value to the index of the texture
			texture_list.push_back(texture);						//add the texture to the texture list
		}
		//if there is a texture map assigned, replace it
		else
		{
			texture_list[((unsigned int*)(uniform_list[index].p_value))[0]] = texture;
		}

	}
	void AttachTextureMap(const char* name, rts_glTextureMap texture)
	{
		int index = get_index(name);
		if(index != -1)		//make sure that the uniform index is valid
			AttachTextureMap(index, texture);
		else
			cout<<"Error finding texture index.  Try linking."<<endl;
	}
	void UpdateGlobalUniforms()									//sends updated uniform information to the GPU
	{
		CHECK_OPENGL_ERROR
		BeginProgram();
		CHECK_OPENGL_ERROR
		unsigned int num = uniform_list.size();
		for(unsigned int i=0; i<num; i++)
			uniform_list[i].submit_to_gpu();
		EndProgram();
	}
	void Init()	//Initialize the shader program
	{
		CHECK_OPENGL_ERROR
		if(id != 0)
			Clean();
		id = glCreateProgram();
		if(id == 0)
			log = "Error getting program ID from OpenGL";
		CHECK_OPENGL_ERROR
	}
	void Clean()
	{
		if(id != 0)
			glDeleteProgram(id);
		id = 0;

		//these are allocated outside the object and can just be cleared
		uniform_list.clear();
		texture_list.clear();

		//delete each shader from OpenGL
		int num_shad = shader_list.size();
		for(int i=0; i<num_shad; i++)
			shader_list[i].Clean();
		//clear the list
		shader_list.clear();
	}
};

#endif