BMP Image Texture Mapping

I want to display four BMP images as textures one by one .
Pls see the code , but i get only one image

char* filename1 = “Globe1.bmp”;
char* filename2 = “Globe2.bmp”;
char* filename3 = “Globe3.bmp”;
char* filename4 = “Globe4.bmp”;

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(240, 240);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);

loadTextureFromFile( filename1 );
glutDisplayFunc(drawScene);

loadTextureFromFile( filename2 );
glutDisplayFunc(drawScene);

loadTextureFromFile( filename3 );
glutDisplayFunc(drawScene);	 

loadTextureFromFile( filename4 );
glutDisplayFunc(drawScene);

glutReshapeFunc(resizeWindow);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0; 

}

What does loadTextureFromFile do?

What was your idea behind calling glutDisplayFunc multiple times with the same callback argument? (As it doesn’t do anything)

Could you please provide the code of your draw function?

Here is the complete code

#include <stdlib.h>
#include <GL/glut.h>

#include “RgbImage.h”

/*

  • Read a texture map from a BMP bitmap file.
    */
    void loadTextureFromFile(char *filename)
    {
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glEnable(GL_DEPTH_TEST);

    RgbImage theTexMap( filename );

    // Pixel alignment: each row is word aligned (aligned to a 4 byte boundary)
    // Therefore, no need to call glPixelStore( GL_UNPACK_ALIGNMENT, … );

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    gluBuild2DMipmaps(GL_TEXTURE_2D, 3,theTexMap.GetNumCols(), theTexMap.GetNumRows(),
    GL_RGB, GL_UNSIGNED_BYTE, theTexMap.ImageData() );

}

/*

  • Draw the texture in the OpenGL graphics window
    */
    void drawScene(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glBegin(GL_QUADS);

    glTexCoord2f(0.0, 0.0);
    glVertex3f(-1.0, -1.0, 0.0);

    glTexCoord2f(0.0, 1.0);
    glVertex3f(-1.0, 1.0, 0.0);

    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0, 1.0, 0.0);

    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0, -1.0, 0.0);

    glEnd();
    glFlush();
    glDisable(GL_TEXTURE_2D);
    }

void resizeWindow(int w, int h)
{
float viewWidth = 1.1;
float viewHeight = 1.1;
glViewport(0, 0, w, h);
h = (h==0) ? 1 : h;
w = (w==0) ? 1 : w;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if ( h < w ) {
viewWidth *= (float)w/(float)h;
}
else {
viewHeight *= (float)h/(float)w;
}
glOrtho( -viewWidth, viewWidth, -viewHeight, viewHeight, -1.0, 1.0 );

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

}

void keyboard (unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
default:
break;
}
}

First of all: I don’t see any texture objects being used in your program, or obvious “bind” calls. This might be related to the problem.

While it should work, this doesn’t belong into loadTextureFromFile (completely illogical, redundant calls, belongs to initialization).

What does this constructor do (besides loading the image from the file)? Does it create a texture object? Does it bind the texture object? Does it upload the data to the texture object?
Can you confirm that the image loading works?

Where is the texture object bound?

The Complete Code is here

RgbImage.h

#ifndef RGBIMAGE_H
#define RGBIMAGE_H

#include <stdio.h>
#include <assert.h>

// Include the next line to turn off the routines that use OpenGL
// #define RGBIMAGE_DONT_USE_OPENGL

class RgbImage
{
public:
RgbImage();
RgbImage( const char* filename );
RgbImage( int numRows, int numCols ); // Initialize a blank bitmap of this size.
~RgbImage();

bool LoadBmpFile( const char *filename );		// Loads the bitmap from the specified file
bool WriteBmpFile( const char* filename );		// Write the bitmap to the specified file

#ifndef RGBIMAGE_DONT_USE_OPENGL
bool LoadFromOpenglBuffer(); // Load the bitmap from the current OpenGL buffer
#endif

long GetNumRows() const { return NumRows; }
long GetNumCols() const { return NumCols; }
// Rows are word aligned
long GetNumBytesPerRow() const { return ((3*NumCols+3)&gt;&gt;2)&lt;&lt;2; }	
const void* ImageData() const { return (void*)ImagePtr; }

const unsigned char* GetRgbPixel( long row, long col ) const;
unsigned char* GetRgbPixel( long row, long col );
void GetRgbPixel( long row, long col, float* red, float* green, float* blue ) const;
void GetRgbPixel( long row, long col, double* red, double* green, double* blue ) const;

void SetRgbPixelf( long row, long col, double red, double green, double blue );
void SetRgbPixelc( long row, long col, 
				   unsigned char red, unsigned char green, unsigned char blue );

// Error reporting. (errors also print message to stderr)
int GetErrorCode() const { return ErrorCode; }
enum {
	NoError = 0,
	OpenError = 1,			// Unable to open file for reading
	FileFormatError = 2,	// Not recognized as a 24 bit BMP file
	MemoryError = 3,		// Unable to allocate memory for image data
	ReadError = 4,			// End of file reached prematurely
	WriteError = 5			// Unable to write out data (or no date to write out)
};
bool ImageLoaded() const { return (ImagePtr!=0); }  // Is an image loaded?

void Reset();			// Frees image data memory

private:
unsigned char* ImagePtr; // array of pixel values (integers range 0 to 255)
long NumRows; // number of rows in image
long NumCols; // number of columns in image
int ErrorCode; // error code

static short readShort( FILE* infile );
static long readLong( FILE* infile );
static void skipChars( FILE* infile, int numChars );
static void RgbImage::writeLong( long data, FILE* outfile );
static void RgbImage::writeShort( short data, FILE* outfile );

static unsigned char doubleToUnsignedChar( double x );

};

inline RgbImage::RgbImage()
{
NumRows = 0;
NumCols = 0;
ImagePtr = 0;
ErrorCode = 0;
}

inline RgbImage::RgbImage( const char* filename )
{
NumRows = 0;
NumCols = 0;
ImagePtr = 0;
ErrorCode = 0;
LoadBmpFile( filename );
}

inline RgbImage::~RgbImage()
{
delete[] ImagePtr;
}

// Returned value points to three “unsigned char” values for R,G,B
inline const unsigned char* RgbImage::GetRgbPixel( long row, long col ) const
{
assert ( row<NumRows && col<NumCols );
const unsigned char* ret = ImagePtr;
long i = rowGetNumBytesPerRow() + 3col;
ret += i;
return ret;
}

inline unsigned char* RgbImage::GetRgbPixel( long row, long col )
{
assert ( row<NumRows && col<NumCols );
unsigned char* ret = ImagePtr;
long i = rowGetNumBytesPerRow() + 3col;
ret += i;
return ret;
}

inline void RgbImage::GetRgbPixel( long row, long col, float* red, float* green, float* blue ) const
{
assert ( row<NumRows && col<NumCols );
const unsigned char* thePixel = GetRgbPixel( row, col );
const float f = 1.0f/255.0f;
red = f(float)((thePixel++));
green = f(float)(
(thePixel++));
blue = f(float)(*thePixel);
}

inline void RgbImage::GetRgbPixel( long row, long col, double* red, double* green, double* blue ) const
{
assert ( row<NumRows && col<NumCols );
const unsigned char* thePixel = GetRgbPixel( row, col );
const double f = 1.0/255.0;
red = f(double)((thePixel++));
green = f(double)(
(thePixel++));
blue = f(double)(*thePixel);
}

inline void RgbImage::Reset()
{
NumRows = 0;
NumCols = 0;
delete[] ImagePtr;
ImagePtr = 0;
ErrorCode = 0;
}

#endif // RGBIMAGE_H

RgbImage.cpp
#include “RgbImage.h”
#ifndef RGBIMAGE_DONT_USE_OPENGL
#include <windows.h>
#include “GL/gl.h”
#endif

RgbImage::RgbImage( int numRows, int numCols )
{
NumRows = numRows;
NumCols = numCols;
ImagePtr = new unsigned char[NumRowsGetNumBytesPerRow()];
if ( !ImagePtr ) {
fprintf(stderr, "Unable to allocate memory for %ld x %ld bitmap.
",
NumRows, NumCols);
Reset();
ErrorCode = MemoryError;
}
// Zero out the image
unsigned char
c = ImagePtr;
int rowLen = GetNumBytesPerRow();
for ( int i=0; i<NumRows; i++ ) {
for ( int j=0; j<rowLen; j++ ) {
*(c++) = 0;
}
}
}

/* ********************************************************************

  • LoadBmpFile
  • Read into memory an RGB image from an uncompressed BMP file.
  • Return true for success, false for failure. Error code is available
  • with a separate call.
    

**********************************************************************/

bool RgbImage::LoadBmpFile( const char* filename )
{
Reset();
FILE* infile = fopen( filename, “rb” ); // Open for reading binary data
if ( !infile ) {
fprintf(stderr, "Unable to open file: %s
", filename);
ErrorCode = OpenError;
return false;
}

bool fileFormatOK = false;
int bChar = fgetc( infile );
int mChar = fgetc( infile );
if ( bChar=='B' && mChar=='M' ) {			// If starts with "BM" for "BitMap"
	skipChars( infile, 4+2+2+4+4 );			// Skip 4 fields we don't care about
	NumCols = readLong( infile );
	NumRows = readLong( infile );
	skipChars( infile, 2 );					// Skip one field
	int bitsPerPixel = readShort( infile );
	skipChars( infile, 4+4+4+4+4+4 );		// Skip 6 more fields

	if ( NumCols&gt;0 && NumCols&lt;=100000 && NumRows&gt;0 && NumRows&lt;=100000  
		&& bitsPerPixel==24 && !feof(infile) ) {
		fileFormatOK = true;
	}
}
if ( !fileFormatOK ) {
	Reset();
	ErrorCode = FileFormatError;
	fprintf(stderr, "Not a valid 24-bit bitmap file: %s.

", filename);
fclose ( infile );
return false;
}

// Allocate memory
ImagePtr = new unsigned char[NumRows*GetNumBytesPerRow()];
if ( !ImagePtr ) {
	fprintf(stderr, "Unable to allocate memory for %ld x %ld bitmap: %s.

",
NumRows, NumCols, filename);
Reset();
ErrorCode = MemoryError;
fclose ( infile );
return false;
}

unsigned char* cPtr = ImagePtr;
for ( int i=0; i&lt;NumRows; i++ ) {
	int j;
	for ( j=0; j&lt;NumCols; j++ ) {
		*(cPtr+2) = fgetc( infile );	// Blue color value
		*(cPtr+1) = fgetc( infile );	// Green color value
		*cPtr = fgetc( infile );		// Red color value
		cPtr += 3;
	}
	int k=3*NumCols;					// Num bytes already read
	for ( ; k&lt;GetNumBytesPerRow(); k++ ) {
		fgetc( infile );				// Read and ignore padding;
		*(cPtr++) = 0;
	}
}
if ( feof( infile ) ) {
	fprintf( stderr, "Premature end of file: %s.

", filename );
Reset();
ErrorCode = ReadError;
fclose ( infile );
return false;
}
fclose( infile ); // Close the file
return true;
}

short RgbImage::readShort( FILE* infile )
{
// read a 16 bit integer
unsigned char lowByte, hiByte;
lowByte = fgetc(infile); // Read the low order byte (little endian form)
hiByte = fgetc(infile); // Read the high order byte

// Pack together
short ret = hiByte;
ret &lt;&lt;= 8;
ret |= lowByte;
return ret;

}

long RgbImage::readLong( FILE* infile )
{
// Read in 32 bit integer
unsigned char byte0, byte1, byte2, byte3;
byte0 = fgetc(infile); // Read bytes, low order to high order
byte1 = fgetc(infile);
byte2 = fgetc(infile);
byte3 = fgetc(infile);

// Pack together
long ret = byte3;
ret &lt;&lt;= 8;
ret |= byte2;
ret &lt;&lt;= 8;
ret |= byte1;
ret &lt;&lt;= 8;
ret |= byte0;
return ret;

}

void RgbImage::skipChars( FILE* infile, int numChars )
{
for ( int i=0; i<numChars; i++ ) {
fgetc( infile );
}
}

/* ********************************************************************

  • WriteBmpFile
  • Write an RGB image to an uncompressed BMP file.
  • Return true for success, false for failure. Error code is available
  • with a separate call.
    

**********************************************************************/

bool RgbImage::WriteBmpFile( const char* filename )
{
FILE* outfile = fopen( filename, “wb” ); // Open for reading binary data
if ( !outfile ) {
fprintf(stderr, "Unable to open file: %s
", filename);
ErrorCode = OpenError;
return false;
}

fputc('B',outfile);
fputc('M',outfile);
int rowLen = GetNumBytesPerRow();
writeLong( 40+14+NumRows*rowLen, outfile );	// Length of file
writeShort( 0, outfile );					// Reserved for future use
writeShort( 0, outfile );
writeLong( 40+14, outfile );				// Offset to pixel data
writeLong( 40, outfile );					// header length
writeLong( NumCols, outfile );				// width in pixels
writeLong( NumRows, outfile );				// height in pixels (pos for bottom up)
writeShort( 1, outfile );		// number of planes
writeShort( 24, outfile );		// bits per pixel
writeLong( 0, outfile );		// no compression
writeLong( 0, outfile );		// not used if no compression
writeLong( 0, outfile );		// Pixels per meter
writeLong( 0, outfile );		// Pixels per meter
writeLong( 0, outfile );		// unused for 24 bits/pixel
writeLong( 0, outfile );		// unused for 24 bits/pixel

// Now write out the pixel data:
unsigned char* cPtr = ImagePtr;
for ( int i=0; i&lt;NumRows; i++ ) {
	// Write out i-th row's data
	int j;
	for ( j=0; j&lt;NumCols; j++ ) {
		fputc( *(cPtr+2), outfile);		// Blue color value
		fputc( *(cPtr+1), outfile);		// Blue color value
		fputc( *(cPtr+0), outfile);		// Blue color value
		cPtr+=3;
	}
	// Pad row to word boundary
	int k=3*NumCols;					// Num bytes already read
	for ( ; k&lt;GetNumBytesPerRow(); k++ ) {
		fputc( 0, outfile );				// Read and ignore padding;
		cPtr++;
	}
}

fclose( outfile );	// Close the file
return true;

}

void RgbImage::writeLong( long data, FILE* outfile )
{
// Read in 32 bit integer
unsigned char byte0, byte1, byte2, byte3;
byte0 = (unsigned char)(data&0x000000ff); // Write bytes, low order to high order
byte1 = (unsigned char)((data>>8)&0x000000ff);
byte2 = (unsigned char)((data>>16)&0x000000ff);
byte3 = (unsigned char)((data>>24)&0x000000ff);

fputc( byte0, outfile );
fputc( byte1, outfile );
fputc( byte2, outfile );
fputc( byte3, outfile );

}

void RgbImage::writeShort( short data, FILE* outfile )
{
// Read in 32 bit integer
unsigned char byte0, byte1;
byte0 = data&0x000000ff; // Write bytes, low order to high order
byte1 = (data>>8)&0x000000ff;

fputc( byte0, outfile );
fputc( byte1, outfile );

}

/*********************************************************************

  • SetRgbPixel routines allow changing the contents of the RgbImage. *
    *********************************************************************/

void RgbImage::SetRgbPixelf( long row, long col, double red, double green, double blue )
{
SetRgbPixelc( row, col, doubleToUnsignedChar(red),
doubleToUnsignedChar(green),
doubleToUnsignedChar(blue) );
}

void RgbImage::SetRgbPixelc( long row, long col,
unsigned char red, unsigned char green, unsigned char blue )
{
assert ( row<NumRows && col<NumCols );
unsigned char* thePixel = GetRgbPixel( row, col );
*(thePixel++) = red;
*(thePixel++) = green;
*(thePixel) = blue;
}

unsigned char RgbImage::doubleToUnsignedChar( double x )
{
if ( x>=1.0 ) {
return (unsigned char)255;
}
else if ( x<=0.0 ) {
return (unsigned char)0;
}
else {
return (unsigned char)(x*255.0); // Rounds down
}
}
// Bitmap file format (24 bit/pixel form) BITMAPFILEHEADER
// Header (14 bytes)
// 2 bytes: “BM”
// 4 bytes: long int, file size
// 4 bytes: reserved (actually 2 bytes twice)
// 4 bytes: long int, offset to raster data
// Info header (40 bytes) BITMAPINFOHEADER
// 4 bytes: long int, size of info header (=40)
// 4 bytes: long int, bitmap width in pixels
// 4 bytes: long int, bitmap height in pixels
// 2 bytes: short int, number of planes (=1)
// 2 bytes: short int, bits per pixel
// 4 bytes: long int, type of compression (not applicable to 24 bits/pixel)
// 4 bytes: long int, image size (not used unless compression is used)
// 4 bytes: long int, x pixels per meter
// 4 bytes: long int, y pixels per meter
// 4 bytes: colors used (not applicable to 24 bit color)
// 4 bytes: colors important (not applicable to 24 bit color)
// “long int” really means “unsigned long int”
// Pixel data: 3 bytes per pixel: RGB values (in reverse order).
// Rows padded to multiples of four.

#ifndef RGBIMAGE_DONT_USE_OPENGL

bool RgbImage::LoadFromOpenglBuffer() // Load the bitmap from the current OpenGL buffer
{
int viewportData[4];
glGetIntegerv( GL_VIEWPORT, viewportData );
int& vWidth = viewportData[2];
int& vHeight = viewportData[3];

if ( ImagePtr==0 ) { // If no memory allocated
	NumRows = vHeight;
	NumCols = vWidth;
	ImagePtr = new unsigned char[NumRows*GetNumBytesPerRow()];
	if ( !ImagePtr ) {
		fprintf(stderr, "Unable to allocate memory for %ld x %ld buffer.

",
NumRows, NumCols);
Reset();
ErrorCode = MemoryError;
return false;
}
}
assert ( vWidth>=NumCols && vHeight>=NumRows );
int oldGlRowLen;
if ( vWidth>=NumCols ) {
glGetIntegerv( GL_UNPACK_ROW_LENGTH, &oldGlRowLen );
glPixelStorei( GL_UNPACK_ROW_LENGTH, NumCols );
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

// Get the frame buffer data.
glReadPixels( 0, 0, NumCols, NumRows, GL_RGB, GL_UNSIGNED_BYTE, ImagePtr);

// Restore the row length in glPixelStorei  (really ought to restore alignment too).
if ( vWidth&gt;=NumCols ) {
	glPixelStorei( GL_UNPACK_ROW_LENGTH, oldGlRowLen );
}	
return true;

}

#endif // RGBIMAGE_DONT_USE_OPENGL

TextureBMP.cpp
#include <stdlib.h>
#include <GL/glut.h>

#include “RgbImage.h”

/*

  • Read a texture map from a BMP bitmap file.
    */
    void loadTextureFromFile(char *filename)
    {
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glEnable(GL_DEPTH_TEST);

    RgbImage theTexMap( filename );

    // Pixel alignment: each row is word aligned (aligned to a 4 byte boundary)
    // Therefore, no need to call glPixelStore( GL_UNPACK_ALIGNMENT, … );

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    gluBuild2DMipmaps(GL_TEXTURE_2D, 3,theTexMap.GetNumCols(), theTexMap.GetNumRows(),
    GL_RGB, GL_UNSIGNED_BYTE, theTexMap.ImageData() );

}

/*

  • Draw the texture in the OpenGL graphics window
    */
    void drawScene(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glBegin(GL_QUADS);

    glTexCoord2f(0.0, 0.0);
    glVertex3f(-1.0, -1.0, 0.0);

    glTexCoord2f(0.0, 1.0);
    glVertex3f(-1.0, 1.0, 0.0);

    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0, 1.0, 0.0);

    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0, -1.0, 0.0);

    glEnd();
    glFlush();
    glDisable(GL_TEXTURE_2D);
    }

void resizeWindow(int w, int h)
{
float viewWidth = 1.1;
float viewHeight = 1.1;
glViewport(0, 0, w, h);
h = (h==0) ? 1 : h;
w = (w==0) ? 1 : w;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if ( h < w ) {
viewWidth *= (float)w/(float)h;
}
else {
viewHeight *= (float)h/(float)w;
}
glOrtho( -viewWidth, viewWidth, -viewHeight, viewHeight, -1.0, 1.0 );

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

}

void keyboard (unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
default:
break;
}
}

char* filename1 = “Globe1.bmp”;
char* filename2 = “Globe2.bmp”;
char* filename3 = “Globe3.bmp”;
char* filename4 = “Globe4.bmp”;

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(240, 240);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);

loadTextureFromFile( filename1 );
glutDisplayFunc(drawScene);

loadTextureFromFile( filename2 );
glutDisplayFunc(drawScene);

loadTextureFromFile( filename3 );
glutDisplayFunc(drawScene);	 

loadTextureFromFile( filename4 );
glutDisplayFunc(drawScene);

glutReshapeFunc(resizeWindow);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0; 

}

Now I can confirm what I already thought and expressed above:

Your code contains no texture object creation or binding whatsoever

You need to create a texture object and bind it in order to operate on it.

My advice is to take a look at an OpenGL(R) tutorial that covers texturing (like this one)