Loading a bitmap texture (OpenGL)

Started by
6 comments, last by Shamino 18 years, 1 month ago
Okay, so I got this bit of code from NeHe, it works alright, havn't had any problems with it so far..

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);				// Declaration For WndProc

AUX_RGBImageRec *LoadBMP(const char *Filename)						// Loads A Bitmap Image
{
	FILE *File=NULL;												// File Handle

	if (!Filename)													// Make Sure A Filename Was Given
	{
		return NULL;												// If Not Return NULL
	}

	File=fopen(Filename,"r");										// Check To See If The File Exists

	if (File)														// Does The File Exist?
	{
		fclose(File);												// Close The Handle
		return auxDIBImageLoad(Filename);							// Load The Bitmap And Return A Pointer
	}

	return NULL;													// If Load Failed Return NULL
}

GLuint LoadGLTexture( const char *filename )						// Load Bitmaps And Convert To Textures
{
	AUX_RGBImageRec *pImage;										// Create Storage Space For The Texture
	GLuint texture = 0;												// Texture ID

	pImage = LoadBMP( filename );									// Loads The Bitmap Specified By filename

	// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
	if ( pImage != NULL && pImage->data != NULL )					// If Texture Image Exists
	{
		glGenTextures(1, &texture);									// Create The Texture

		// Typical Texture Generation Using Data From The Bitmap
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, pImage->sizeX, pImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, pImage->data);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

		free(pImage->data);											// Free The Texture Image Memory
		free(pImage);												// Free The Image Structure
	}

	return texture;													// Return The Status
}

I notice that it uses auxDIBImageLoad to actually load the bitmap... The way my program is set up, I want to create a texture object with its own loading function.. Something like

class BMPTexture
{
// storage structs
// Loading Code
};
I kinda want to mirror this setup (snipped from my MS3DModel definition)

class MS3DModel
{
public:

	MS3DModel();

	virtual ~MS3DModel();

	struct Vertex
	{
		char BoneID;
		float Location[3];
	};

	int NumVertices;
	Vertex *Vertices;

	struct Triangle
	{
		float VertexNormals[3][3];
		float Textures1[3], Textures2[3];
		int VertexIndices[3];
	};

	int NumTriangles;
	Triangle *Triangles;

	struct Mesh
	{
		int MaterialIndex;
		int NumTriangles;
		int *TriangleIndices;
	};

	int NumMeshes;
	Mesh *Meshes;

	struct Material
	{
		float Ambient[4], Diffuse[4], Specular[4], Emissive[4];
		float Shininess;
		GLuint Texture;
		char *TextureFilename;
	};

	int NumMaterials;
	Material *Materials;

	bool Load( const std::string & name );
	void ReloadTextures();
	void Draw();
};

Aside from the drawing function and reloadTextures function, and of course different storage structures, I want to mirror this setup, I find it works quite nicely with the resource manager I've created, shown here:

template< typename T_ >
class Resource_Manager
{  

public:

	typedef T_ value_type; // std library convention 

	typedef boost::shared_ptr<T_> Resource_Ptr;
	typedef boost::weak_ptr<T_> Resource_Observer;
	typedef std::map< std::string, Resource_Ptr > Resource_Map;

	Resource_Manager<T_>() {};
	~Resource_Manager<T_>() {};

	Resource_Observer Request_Resource(const std::string & name)
	{
		Resource_Map::iterator  it = mResources.find(name);

		if (it == mResources.end())
		{
			Resource_Ptr Raw_Resource(new T_);
			Raw_Resource->Load(name);
			mResources.insert(std::make_pair(name, Raw_Resource));
			Resource_Observer Resource(Raw_Resource);
			return Resource;
		}
		else
		{
			return Resource_Observer(it->second);
		}
	}

	void Request_Resource_Removal(const std::string & name)
	{
		Resource_Map::iterator it = mResources.find(name);

		if (it != mResources.end())
		{
			mResources.erase(it);
		}
	}

private:
	Resource_Map  mResources;
};

As you can see, it returns a resource observer weak pointer, but the actual loading of the bitmap returns a texture ID. While applying textures via ID is what I want to be able to do, you kinda have to in opengl. HMmmm... Looking at the loading code more closely, LoadBMP actually returns a pointer to the actual bitmap, while the GLLoadTexture func creates the texID.. The reason I want to use my manager to do all this stuff is, not to sound boastful, basically that my resource manager can do a cleaner job than the auxDIBImageLoad function. I don't really have to worry about deleting stuff with my resource manager, it is a nice way to do things IMO.. I guess my dilemma is this.. My resource manager handles pointers, it doesn't handle ID's, thus I can't mix ID creation in with my texture manager, unless maybe I'm returning an observer with a tex ID in it..... Ah yes, that's it. If I mirror the MS3D setup I can easily add an ID in the info structs and then access the ID of the texture object with methods in that class.. So I'm guessing when I get down to it, it looks like this: Resource_Manager<BMPTexture> Textures; Textures.Request_Resource("texture name here"); Then I can access the ID via the return value of the request resource method, which is a resource observer.. Okay, is this making sense to you guys? Am I in rightful reason to want to avoid the auxDIBImageLoad function? Is what I'm proposing making sense to you guys? Thanks.
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Advertisement
I can't comment on whether using your resource manager is better, but since you seem to think so I'll believe you =). The answer to your problem is to have GLLoadImage return a struct containing the texture ID and the data, and maybe some metadata as well if you need it later.

My TexInfo structure (Delphi) looked like this:
TTexInfo = record  // returned by GLLoadTexture TexInx: Integer; Data: Pointer; DataSize: Integer;end;TTexture = record        // created by the level loader TexInfo: TTexInfo; Height, Width: Integer;end;
I'm thinking something like this...

class BitmapTexture{public:	struct BMTexture	{		// low level information	};	struct TextureInfo	{		GLuint TexID;		GLuint TexWidth;		GLuint TexHeight;		// high level information	};private:	bool Load(std::string & name)	{		// Do loading stuff, return a true if success, false if fail..	}};


Thanks for the bit of insight :)
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Quote:Original post by Shamino
// Do loading stuff, return a true if success, false if fail..
I personally prefer thrown exceptions, but that's just me [wink].
Rob Loach [Website] [Projects] [Contact]
Eh, I don't think I want the program to die if a single bitmap fails to load, yknow?
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
So does anyone know where I can some generic code that loads a bitmap and fills out some structs of information? The idea here is to get away from auxDIBImageLoad...

Anyone? :)
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Header:

#ifndef CTEXTURE_H#define CTEXTURE_H#include <windows.h>        // Windows header file.#include <stdio.h>          // Standard input/output.#define BITMAP_ID 0x4D42	// The universal bitmap IDclass CBMPLoader{   public:      CBMPLoader();      ~CBMPLoader();      bool LoadBitmap(char *filename); // Load a .bmp image file.      void FreeImage();                // Delete a image.      void transparent(int* g_keyColor);      unsigned int ID;                 // Texture ID.      int imageWidth;                  // Width of a texture.      int imageHeight;                 // Height of a texture.      unsigned char *image;            // Texture image.      unsigned char *TRANimage;};#endif



Footer Source:

#include "CBMPLoader.h"CBMPLoader::CBMPLoader(){   // Give everything default values.  image = 0;  TRANimage = 0;  imageWidth = 0;  imageHeight = 0;}CBMPLoader::~CBMPLoader(){   FreeImage();}bool CBMPLoader::LoadBitmap(char *file){	FILE *pFile = 0;	// These will hold the bitmap header and file information.	BITMAPINFOHEADER bitmapInfoHeader;	BITMAPFILEHEADER header;   // This will be used to swap the image colors from BGR to RGB.	unsigned char textureColors = 0;   // Open the file and make sure no errors.	pFile = fopen(file, "rb");	if(pFile == 0) return false;	// Read in the bitmap header info into the BITMAPFILEHEADER variable.	fread(&header, sizeof(BITMAPFILEHEADER), 1, pFile);	// Make sure this is a real bitmap by checking the ID.	if(header.bfType != BITMAP_ID)	   {		   fclose(pFile);		   return false;	   }	// Read in the second header info into the BITMAPINFOHEADER variable.	fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pFile);	// Save the width and height.	imageWidth = bitmapInfoHeader.biWidth;   imageHeight = bitmapInfoHeader.biHeight;   // Make sure we read a size out of it.   if(bitmapInfoHeader.biSizeImage == 0)      bitmapInfoHeader.biSizeImage = bitmapInfoHeader.biWidth *      bitmapInfoHeader.biHeight * 3;	// Place the pointer in front of where the image data starts.	fseek(pFile, header.bfOffBits, SEEK_SET);	// Dynamically create enough memory for the image.	image = new unsigned char[bitmapInfoHeader.biSizeImage];	// Error checking.  Make sure the memory was allocated.	if(!image)	   {		   delete[] image;		   fclose(pFile);		   return false;	   }	// Read in the image.	fread(image, 1, bitmapInfoHeader.biSizeImage, pFile);	// Bitmaps are saved in BGR format so we will make the image RGB by...	for(int index = 0; index < (int)bitmapInfoHeader.biSizeImage; index+=3)	   {		   textureColors = image[index];		   image[index] = image[index + 2];		   image[index + 2] = textureColors;	   }   // Close the file and return the image.	fclose(pFile);	return true;}void CBMPLoader::transparent(int* g_keyColor){    int imageSize_RGB  = imageWidth * imageHeight * 3;    int imageSize_RGBA = imageWidth * imageHeight * 4;     //allocate buffer for a RGBA image    TRANimage = new unsigned char[imageSize_RGBA];//     Loop through the original RGB image buffer and copy it over to the//     new RGBA image buffer setting each pixel that matches the key color//     transparent.    int i, j;    for(i = 0, j = 0; i < imageSize_RGB; i += 3, j += 4 )    {//         Does the current pixel match the selected color key?        if( image   == g_keyColor[0] &&            image[i+1] == g_keyColor[1] &&            image[i+2] == g_keyColor[2] )        {            TRANimage[j+3] = 0;   // If so, set alpha to fully transparent.        }        else        {            TRANimage[j+3] = 255; // If not, set alpha to fully opaque.        }        TRANimage[j]   = image;        TRANimage[j+1] = image[i+1];        TRANimage[j+2] = image[i+2];    }}void CBMPLoader::FreeImage(){   // When the application is done delete all memory.   if(image)      {         delete[] image;         image = NULL;      }    if(TRANimage)      {         delete[] TRANimage;         TRANimage = NULL;      }}
Very cool response, was a shame you're anonymous, no rate++ :(...

Anyways, thanks for the code snippets, I should be able to add this to my manager quite easily.. All I gotta do is modify the load function to actually create an openGL texture ID :d..
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------

This topic is closed to new replies.

Advertisement