gl_texture.h 9.63 KB
#ifndef STIM_GL_TEXTURE_H
#define STIM_GL_TEXTURE_H

#include <math.h>
#include <iostream>
#include <vector>
#include "../grids/image_stack.h"
//Visual Studio requires GLEW
#ifdef _WIN32
	#include <GL/glew.h>
#endif
//#include <GL/glut.h>
#include <stim/gl/error.h>	
namespace stim{

/*
class gl_texture
	Uses image_stack class in order to create a texture object.
*/

template<typename T, typename F = float>
class gl_texture : public virtual image_stack<T, F>
{
	protected:
		//std::string path;
		GLuint texID;				//OpenGL object
		GLenum texture_type;			//1D, 2D, 3D
		GLint interpolation;
		GLint wrap;	
		GLenum cpu_type;
		GLenum gpu_type;
		GLenum format;					//format for the texture (GL_RGBA, GL_LUMINANCE, etc.)
		using image_stack<T>::R;
		//using image_stack<T>::S;
		using image_stack<T>::ptr;

		///	Sets the internal texture_type, based on the data dimensions
		void setTextureType(){
			if (R[3] > 1)						//if the third dimension is greater than 1
				texture_type = GL_TEXTURE_3D;	//this is a 3D texture
			else if (R[2] > 1)					//if the second dimension is greater than 1
				texture_type = GL_TEXTURE_2D;	//this is a 2D texture
			else if (R[1] > 1)					//if the dimension value is greater than 1
				texture_type = GL_TEXTURE_1D;	//this is a 1D texture
		}

		//initializes important variables
		void init() {
			texID = 0;							//initialize texture ID to zero, default if OpenGL returns an error
			//memset(R, 0, sizeof(size_t));
			//memset(grid<T, 4, F>::S, 0, sizeof(F));
		}

		//guesses the color format of the texture
		GLenum guess_format(){
			size_t channels = R[0];
			switch(channels){
			case 1:
				return GL_LUMINANCE;
			case 2:
				return GL_RG;
			case 3:
				return GL_RGB;
			case 4:
				return GL_RGBA;
			default:
				std::cout<<"Error in stim::gl_texture - unable to guess texture format based on number of channels ("<<R[4]<<")"<<std::endl;
				exit(1);
			}
		}

		//guesses the OpenGL CPU data type based on T
		GLenum guess_cpu_type(){
			// The following is C++ 11 code, but causes problems on some compilers (ex. nvcc). Below is my best approximation to a solution

			//if(std::is_same<T, unsigned char>::value)	return CV_MAKETYPE(CV_8U, (int)C());
			//if(std::is_same<T, char>::value)			return CV_MAKETYPE(CV_8S, (int)C());
			//if(std::is_same<T, unsigned short>::value)	return CV_MAKETYPE(CV_16U, (int)C());
			//if(std::is_same<T, short>::value)			return CV_MAKETYPE(CV_16S, (int)C());
			//if(std::is_same<T, int>::value)				return CV_MAKETYPE(CV_32S, (int)C());
			//if(std::is_same<T, float>::value)			return CV_MAKETYPE(CV_32F, (int)C());
			//if(std::is_same<T, double>::value)			return CV_MAKETYPE(CV_64F, (int)C());

			if(typeid(T) == typeid(unsigned char))		return GL_UNSIGNED_BYTE;
			if(typeid(T) == typeid(char))				return GL_BYTE;
			if(typeid(T) == typeid(unsigned short))		return GL_UNSIGNED_SHORT;
			if(typeid(T) == typeid(short))				return GL_SHORT;
			if(typeid(T) == typeid(unsigned int))		return GL_UNSIGNED_INT;
			if(typeid(T) == typeid(int))				return GL_INT;
			if(typeid(T) == typeid(float))				return GL_FLOAT;

			std::cout<<"ERROR in stim::gl_texture - no valid data type found"<<std::endl;
			exit(1);
		}

		//Guesses the "internal format" of the texture to closely approximate the original format
		GLint guess_gpu_type(){
			switch(format){
			case GL_LUMINANCE:
				switch(cpu_type){
				case GL_BYTE:
				case GL_UNSIGNED_BYTE:
					return GL_LUMINANCE8;
				case GL_SHORT:
				case GL_UNSIGNED_SHORT:
					return GL_LUMINANCE16;
				case GL_INT:
				case GL_UNSIGNED_INT:
					return GL_LUMINANCE32I_EXT;
				case GL_FLOAT:
					return GL_LUMINANCE32F_ARB;
				default:
					std::cout<<"error in stim::gl_texture - unable to guess GPU internal format"<<std::endl;
					exit(1);
				}
			case GL_RGB:
				switch(cpu_type){
				case GL_BYTE:
				case GL_UNSIGNED_BYTE:
					return GL_RGB8;
				case GL_SHORT:
				case GL_UNSIGNED_SHORT:
					return GL_RGB16;
				case GL_INT:
				case GL_UNSIGNED_INT:
					return GL_RGB32I;
				case GL_FLOAT:
					return GL_RGB32F;
				default:
					std::cout<<"error in stim::gl_texture - unable to guess GPU internal format"<<std::endl;
					exit(1);
				}
			case GL_RGBA:
				switch(cpu_type){
				case GL_BYTE:
				case GL_UNSIGNED_BYTE:
					return GL_RGBA8;
				case GL_SHORT:
				case GL_UNSIGNED_SHORT:
					return GL_RGBA16;
				case GL_INT:
				case GL_UNSIGNED_INT:
					return GL_RGBA32I;
				case GL_FLOAT:
					return GL_RGBA32F;
				default:
					std::cout<<"error in stim::gl_texture - unable to guess GPU internal format"<<std::endl;
					exit(1);
				}
			default:
				std::cout<<"error in stim::gl_texture - unable to guess GPU internal format"<<std::endl;
				exit(1);
			}
		}
		///	creates this texture in the current OpenGL context
		void generate_texture(){
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			CHECK_OPENGL_ERROR
			glGenTextures(1, &texID);
			CHECK_OPENGL_ERROR
			glBindTexture(texture_type, texID);
			CHECK_OPENGL_ERROR
			glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, interpolation);
			CHECK_OPENGL_ERROR
			glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, interpolation);
			CHECK_OPENGL_ERROR
			switch(texture_type){
				case GL_TEXTURE_3D:
					glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, wrap);
					glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, wrap);
					glTexParameteri(texture_type, GL_TEXTURE_WRAP_R, wrap);
					glTexImage3D(texture_type, 0, gpu_type, (GLsizei)R[1], (GLsizei)R[2], (GLsizei)R[3], 0, format, cpu_type, ptr);
					break;
				case GL_TEXTURE_2D:
					glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, wrap);
					CHECK_OPENGL_ERROR
					glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, wrap);
					CHECK_OPENGL_ERROR
					glTexImage2D(texture_type, 0, gpu_type, (GLsizei)R[1], (GLsizei)R[2], 0, format, cpu_type, ptr);
					CHECK_OPENGL_ERROR
					break;
				case GL_TEXTURE_1D:
					glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, wrap);
					CHECK_OPENGL_ERROR
					glTexImage1D(texture_type, 0, gpu_type, (GLsizei)R[1], 0, format, cpu_type, ptr);
					CHECK_OPENGL_ERROR
					break;
				default:
					std::cout<<"Error in stim::gl_texture - unrecognized texture target when generating texture"<<std::endl;
					exit(1);
					break;
			}
			CHECK_OPENGL_ERROR
		}
		void guess_parameters(){
			setTextureType();									//set the texture type: 1D, 2D, 3D
			format = guess_format();							//guess the texture format based on the number of image channels
			cpu_type = guess_cpu_type();						//guess the CPU type based on the template
			gpu_type = guess_gpu_type();						//guess the GPU type based on the format and template
		}

	public:

		///default constructor
		gl_texture(	GLint interp = GL_LINEAR,					//default to linear interpolation
					GLint twrap = GL_REPEAT)					//default repeating the texture at the edges					
						: image_stack<T>() {
			init();												//initialize the texture with NULL values
			interpolation = interp;								//store the interpolation type
			wrap = twrap;										//store the wrap type
		}

		///@param is a mask indicating the files to load
		///Creates an instance of the gl_texture object and initializes it with a file list

		gl_texture(std::string file_mask, GLint interp = GL_LINEAR, GLint twrap = GL_REPEAT){
			init();
			interpolation = interp;								//store interpolation type
			wrap = twrap;										//store wrap type
			load_images(file_mask);
		}

		///Creates an instance of gl_texture and initializes with a file list
		///@param file_list is a list of files
		///@param interp is the type of texture interpolation (GL_LINEAR, GL_NEAREST)
		///@param twrap is the type of texture wrapping
		gl_texture(std::vector<std::string> file_list, GLint interp = GL_LINEAR, GLint twrap = GL_REPEAT){
			init();
			interpolation = interp;
			wrap = twrap;
			load_images(file_list);
		}

		///Attaches the texture to the current OpenGL context and makes it ready to render
		void attach(){
			if(texID == 0) generate_texture();					//generate the texture if it doesn't already exist
			else{
				std::cout<<"Texture has already been attached to a context."<<std::endl;
				exit(1);
			}
		}

		//binds a texture to be the current render source
		void bind(){
			glBindTexture(texture_type, texID);					//bind the texture to the appropriate texture target
			CHECK_OPENGL_ERROR
		}

		///returns the dimentions of the data in the x, y, z directions. 
		vec<int> getSize(){
			stim::vec<int> size(R[1], R[2], R[3]);
			return size;
		}

		void getSize(size_t& x, size_t& y, size_t& z) {
			x = R[0]; y = R[1]; z = R[2];
		}

		///@param x size of the voxel in x direction
		///@param y size of the voxel in y direction
		///@param z size of the voxel in z direction
		///	Sets the dimenstions of the voxels.
		void setSpacing(float sx, float sy, float sz){
			grid<T, 4, F>::S[1] = sx;
			grid<T, 4, F>::S[2] = sy;
			grid<T, 4, F>::S[3] = sz;
		}

		///Returns a stim::vec that contains the x, y, z sizes of the voxel.
		vec<float> getDims(){
			vec<float> dims(grid<T, 4, F>::S[1], grid<T, 4, F>::S[2], grid<T, 4, F>::S[3]);
			return dims;
		}

		/// Loads a series of files specified by a list of strings
		/// @param file_list is the vector of file names as strings
		void load_images(std::vector<std::string> file_list){
			image_stack<T, F>::load_images(file_list);			//load the images
			guess_parameters();
		}

		///@param file_mask specifies the file(s) to be loaded
		///	Sets the path and calls the loader on that path.
		void load_images(std::string file_mask){
			image_stack<T>::load_images(file_mask);				//load images
			guess_parameters();
		}

		///	Returns the GLuint id of the texture created by/associated with the 
		///	instance of the gl_texture class.				
		GLuint getTexture(){
			return texID;
		}
		
		
		T* getData(){
			return ptr;
		}
		
		
	};
}


	
	


#endif