Texture Mapping Quads in OpenGL

This program will use the following features from OpenGL: glGenTextures, glBindTexture, glTexImage2D, glTexParameteri and glTexCoord2f.

To use textures in OpenGL, you have to load the image data into your programs memory. For DIBs and BMPs, you probably want to load them into a buffer of type unsigned char *. DIBs and BMPs requires special routine for proper loading. In this example, we are using the RAW file format.

There might be many textures that will be used in your program. In OpenGL, every textures are identified by unique integers. These integers are generated with a call to glGenTextures. You still have to call glGenTextures even if you have only one texture.

For each texture, we first make a call to glBindTexture to tell OpenGL which texture we will be processing. Then, we call glTexImage2D to tell OpenGL the details regarding the texture, such as the width, height, format of the data and the image itself. Because the image size most probably does not fit exactly as the width and height of the QUAD, we have to specify the filter functions to use with the function glTexParameteri when the image is smaller than the targetted QUAD, and when the image is larger than the target QUAD.

Finally, we call glTexCoord2f to specify the texture coordinates (u,v), and call glVertex3f (x,y,z) for the corresponding vertex of the QUAD.

Make sure that GL_TEXTURE_2D is enabled.

#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h> // for file handling
#include <io.h>
#include <glut.h>

void Initialize(void);
void Display(void);
void Reshape(int w, int h);
void Keyboard (unsigned char key, int x, int y);
void DrawFloor(void);
void DrawTexturedCube(GLdouble size,
					  GLdouble x_offset,GLdouble y_offset, GLdouble z_offset,
					  GLint rot_x, GLint rot_y, GLint rot_z,
					  GLint tex_front,
					  GLint tex_back,
					  GLint tex_top,
					  GLint tex_bottom,
					  GLint tex_left,
					  GLint tex_right
					  );
bool LoadTextures(void);
unsigned char * LoadRAW(char * filename);

#define NUM_TEXTURES 1 // number of textures
char * TextureFiles[] = {"4sides.raw",0};

bool AntialiasToggle = false;
bool TextureToggle = true;

GLuint TextureMap[NUM_TEXTURES]; // for the texture names


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	Initialize();
	glutMainLoop();
	return 0;
}

void Initialize(void)
{
	int argc;
	char * argv[] = {"dummy",0};
	argc = 1;
	glutInit(&argc, (char **)&argv[0]);
	glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH|GLUT_ALPHA);
	glutInitWindowSize(300, 300);
	glutInitWindowPosition(-1, -1); // Let window to determine the startup position
	glutCreateWindow("Two Texture Mapped Cubes");
	
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);  // Sets the background color - R, G, B, and alpha
	glShadeModel(GL_SMOOTH); // One of GL_SMOOTH or GL_FLAT
	glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE);
	glColorMaterial(GL_FRONT,GL_SPECULAR); // Makes the coloring work for basic primitives
	glCullFace(GL_BACK);
	
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0); // Enable a single light source
	glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_CULL_FACE);
	glEnable(GL_TEXTURE_2D);
	
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Nicest perspective output
	
	// Register GLUT callback functions
	glutDisplayFunc(Display); // Register Display() function
	glutReshapeFunc(Reshape); // Register Reshape() function
	glutKeyboardFunc(Keyboard); // Register Keyboard() function

	// Load the textures
	if (!LoadTextures())
	{
		exit(1);
	}
}

bool LoadTextures(void)
{
	unsigned char * image;
	int i;
	glGenTextures(NUM_TEXTURES, &TextureMap[0]);  // Generate texture names
	for (i=0;i<NUM_TEXTURES;i++)
	{
		glBindTexture(GL_TEXTURE_2D, TextureMap[i]);
		image = LoadRAW(TextureFiles[i]);
		if (image)
		{
			glTexImage2D(GL_TEXTURE_2D, 0, 3, (GLint)128, (GLint)128, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
			free(image);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);	// Linear Filtering
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	// Linear Filtering
		}
		else return false;
	}
	return true;
}

unsigned char * LoadRAW(char * filename)
{
	unsigned char * ImageData = NULL;
	int PixelPos;
	FILE * Fin;
	
	unsigned char Buffer[256];
	long ByteCount;
	
	if ((Fin = fopen(filename,"rb"))!=NULL) // open file for read only, binary
	{
		while ((ByteCount = fread(Buffer,1,256,Fin))>0)
		{
			if (ImageData)
			{
				ImageData = (BYTE *)realloc(ImageData,ByteCount + PixelPos);
				if (!ImageData)
				{
					MessageBox(NULL,"Error allocating memory.","",MB_OK);
					fclose(Fin);
					return NULL;
				}
			}
			else
			{
				ImageData = (BYTE *)malloc(ByteCount);
				PixelPos = 0;
				if (!ImageData)
				{
					MessageBox(NULL,"Error allocating memory.","",MB_OK);
					fclose(Fin);
					return NULL;
				}
			}
			memcpy(ImageData + PixelPos,Buffer,ByteCount);
			PixelPos= PixelPos + ByteCount;
		}
		fclose(Fin);
		return ImageData;
	}
	else
	{
		MessageBox(NULL,"Error openning file.","",MB_OK);
		return NULL;
	}
}

void Reshape(int w, int h)
{
	double range = 2;
	glViewport(0,0,w,h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-range,range,-range,range,-range,range);
	gluPerspective(30,1,1,20);
	glTranslatef(0,0,-4);
}

void Keyboard (unsigned char key, int x, int y)
{
	// Keyboard events can be intepreted here with a switch-case structure
	switch (key)
	{
	case 'a':
		AntialiasToggle = !AntialiasToggle;
		glutPostRedisplay();
		break;
	case 't':
		TextureToggle = !TextureToggle;
		glutPostRedisplay();
		break;
	default:
		break;
	}
}

void Display(void)
{
	if (AntialiasToggle)
	{
		glClear(GL_COLOR_BUFFER_BIT);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_BLEND);
		glEnable(GL_POLYGON_SMOOTH);
	}
	else
	{
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
		glEnable(GL_DEPTH_TEST);
		glDisable(GL_BLEND);
		glDisable(GL_POLYGON_SMOOTH);
	}
	if (TextureToggle)
		glEnable(GL_TEXTURE_2D);
	else
		glDisable(GL_TEXTURE_2D);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	DrawTexturedCube(1.0f, // size
					 1.0f,0.5f,0, // offset
					 30,30,0, // rotation
					 0,0,0,0,0,0); // textures
	DrawTexturedCube(1.0f,
					 -1.0f,0.5f,0,
					 30,30,0,
					 0,0,0,0,0,0); // textures
	
	glutSwapBuffers(); // swap the display buffer to show the new results
}

void DrawTexturedCube(GLdouble size,
					  GLdouble x_offset, GLdouble y_offset, GLdouble z_offset,
					  GLint rot_x, GLint rot_y, GLint rot_z,
					  GLint tex_front,
					  GLint tex_back,
					  GLint tex_top,
					  GLint tex_bottom,
					  GLint tex_left,
					  GLint tex_right
					  )
{
	GLdouble HL; // half length for the sides of cube
	HL = size / 2.0f;
	glPushMatrix();
	glTranslatef(x_offset,y_offset,z_offset);
	glPushMatrix();
	glRotatef(rot_x,1,0,0);
	glRotatef(rot_y,0,1,0);
	glRotatef(rot_z,0,0,1);
	
	// Front Face
	glBindTexture(GL_TEXTURE_2D, TextureMap[tex_front]);
	glBegin(GL_QUADS);
		if (TextureToggle) glTexCoord2f(0.0f, 0.0f); glVertex3f(-HL,-HL, HL);
		if (TextureToggle) glTexCoord2f(1.0f, 0.0f); glVertex3f( HL,-HL, HL);
		if (TextureToggle) glTexCoord2f(1.0f, 1.0f); glVertex3f( HL, HL, HL);
		if (TextureToggle) glTexCoord2f(0.0f, 1.0f); glVertex3f(-HL, HL, HL);
	glEnd();
	// Back Face
	glBindTexture(GL_TEXTURE_2D, TextureMap[tex_back]);
	glBegin(GL_QUADS);
		if (TextureToggle) glTexCoord2f(1.0f, 0.0f); glVertex3f(-HL,-HL,-HL);
		if (TextureToggle) glTexCoord2f(1.0f, 1.0f); glVertex3f(-HL, HL,-HL);
		if (TextureToggle) glTexCoord2f(0.0f, 1.0f); glVertex3f( HL, HL,-HL);
		if (TextureToggle) glTexCoord2f(0.0f, 0.0f); glVertex3f( HL,-HL,-HL);
	glEnd();
	// Top Face
	glBindTexture(GL_TEXTURE_2D, TextureMap[tex_top]);
	glBegin(GL_QUADS);
		if (TextureToggle) glTexCoord2f(0.0f, 1.0f); glVertex3f(-HL, HL,-HL);
		if (TextureToggle) glTexCoord2f(0.0f, 0.0f); glVertex3f(-HL, HL, HL);
		if (TextureToggle) glTexCoord2f(1.0f, 0.0f); glVertex3f( HL, HL, HL);
		if (TextureToggle) glTexCoord2f(1.0f, 1.0f); glVertex3f( HL, HL,-HL);
	glEnd();
	// Bottom Face
	glBindTexture(GL_TEXTURE_2D, TextureMap[tex_bottom]);
	glBegin(GL_QUADS);
		if (TextureToggle) glTexCoord2f(1.0f, 1.0f); glVertex3f(-HL,-HL,-HL);
		if (TextureToggle) glTexCoord2f(0.0f, 1.0f); glVertex3f( HL,-HL,-HL);
		if (TextureToggle) glTexCoord2f(0.0f, 0.0f); glVertex3f( HL,-HL, HL);
		if (TextureToggle) glTexCoord2f(1.0f, 0.0f); glVertex3f(-HL,-HL, HL);
	glEnd();
	// Left Face
	glBindTexture(GL_TEXTURE_2D, TextureMap[tex_left]);
	glBegin(GL_QUADS);
		if (TextureToggle) glTexCoord2f(0.0f, 0.0f); glVertex3f(-HL,-HL,-HL);
		if (TextureToggle) glTexCoord2f(1.0f, 0.0f); glVertex3f(-HL,-HL, HL);
		if (TextureToggle) glTexCoord2f(1.0f, 1.0f); glVertex3f(-HL, HL, HL);
		if (TextureToggle) glTexCoord2f(0.0f, 1.0f); glVertex3f(-HL, HL,-HL);
	glEnd();
	// Right face
	glBindTexture(GL_TEXTURE_2D, TextureMap[tex_right]);
	glBegin(GL_QUADS);
		if (TextureToggle) glTexCoord2f(1.0f, 0.0f); glVertex3f( HL,-HL,-HL);
		if (TextureToggle) glTexCoord2f(1.0f, 1.0f); glVertex3f( HL, HL,-HL);
		if (TextureToggle) glTexCoord2f(0.0f, 1.0f); glVertex3f( HL, HL, HL);
		if (TextureToggle) glTexCoord2f(0.0f, 0.0f); glVertex3f( HL,-HL, HL);
	glEnd();
	glPopMatrix();
	glPopMatrix();	
}
: : go back : :