Skip to main content
GameDev.net gamedev.net
🔒 Locked

Texture mapping

Started by Endar Oct 15, 2005 at 11:23 PM 9 replies 1.4k views
Original Post
Endar
Endar
I'm having some trouble mapping a bitmap to one side of a cube. Problem 1: When I'm mapping, the cube rotates really slowly. I've found that it's becuase of the 'glTexParameteri' function calls.

/**
 * Base class that represents a texture.
 */
class ITexture	:	public IMBase
{
protected:

	unsigned int m_texDataSize;	///< The size of the data (in bytes)
	unsigned char* m_texData;	///< The texture data
	video::dimension2d m_size;	///< The size of the picture (width and height)

public:

	/**
	 * Constructor
	 * \param r A reference to a CReadFile object that is open to a valid file.
	 * \param d An unsigned char pointer to the image data.
	 * Note:: A seperate copy of the data is made for the texture, so the memory
	 * pointed to by the param can be free'd after being passed here.
	 */
	ITexture(video::dimension2d& image_size, unsigned int size, unsigned char* d)
		:	m_size(image_size), m_texDataSize(size)
	{
		m_texData = new unsigned char[size];
		memcpy(m_texData, d, size);		// copy the data across
	}

	/**
	 * Destructor
	 */
	virtual ~ITexture()
	{
		delete m_texData;
	}

	/**
	 * Return the height and width of the texture.
	 * \return The height and width of the texture.
	 */
	video::dimension2d getSize() const
	{
		return m_size;
	}

	/**
	 * Apply the texture.
	 */
	virtual void apply() = 0;

};

/**
 * A wrapper class to represent an OpenGL texture.
 */
class COpenGLTexture	:	public ITexture
{
private:
	unsigned int m_texture;		///< The texture object

public:
	/**
	 * Constructor
	 * \param texture The openGL reference to this texture
	 * \param image_size The dimensions of the image
	 * \param size The size of the image data (in bytes)
	 * \param d A pointer to the image data
	 */
	COpenGLTexture(unsigned int texture, video::dimension2d& image_size, unsigned int size, unsigned char* d)
		:	ITexture(image_size, size, d), m_texture(texture)
	{

	}

	/**
	 * Apply the texture.
	 */
	void apply()
	{
		// apply the texture
		glBindTexture(GL_TEXTURE_2D, m_texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (int)m_size.width, (int)m_size.height,
					 0, GL_RGB, GL_UNSIGNED_BYTE, m_texData);
	}
};




Where should I be putting these function calls? Problem 2: I'm not seeing the texture on the cube (rotating) when I try to texture. I figure its either my init function, my vertices and the related tex coords, or my bitmap loading function, so I'm providing them here for fixing, hints, tips or criticism. Whatever you feel like dishing out (as long as its related to my code :D)

// The cube vertices.
// The order of params is (position, normal, color, u, v)
// The normal really isn't being used right now. So, its probably kinda
// (completely messed up)
util::array<scene::CVertex3d> arr;
arr.resize(8);
arr[0] = scene::CVertex3d( util::CVector3d(-0.5f,  0.5f,  0.5f), util::CVector3d(-0.5f,  0.5f,  0.5f), 
		video::CColor(1.0f,0.0f,0.0f), 0.0f, 0.0f);
arr[1] = scene::CVertex3d( util::CVector3d(-0.5f, -0.5f,  0.5f), util::CVector3d(-0.5f, -0.5f,  0.5f), 
		video::CColor(0.0f,1.0f,0.0f), 0.0f, 1.0f);
arr[2] = scene::CVertex3d( util::CVector3d( 0.5f,  0.5f,  0.5f), util::CVector3d( 0.5f,  0.5f,  0.5f), 
		video::CColor(0.0f,0.0f,1.0f), 1.0f, 0.0f);
arr[3] = scene::CVertex3d( util::CVector3d( 0.5f, -0.5f,  0.5f), util::CVector3d( 0.5f, -0.5f,  0.5f), 
		video::CColor(1.0f,1.0f,0.0f), 1.0f, 1.0f);
arr[4] = scene::CVertex3d( util::CVector3d( 0.5f, -0.5f, -0.5f), util::CVector3d( 0.5f, -0.5f, -0.5f), 
		video::CColor(1.0f,0.0f,1.0f), 0.0f, 0.0f);
arr[5] = scene::CVertex3d( util::CVector3d( 0.5f,  0.5f, -0.5f), util::CVector3d( 0.5f,  0.5f, -0.5f), 
		video::CColor(0.0f,1.0f,1.0f), 0.0f, 0.0f);
arr[6] = scene::CVertex3d( util::CVector3d(-0.5f,  0.5f, -0.5f), util::CVector3d(-0.5f,  0.5f, -0.5f), 
		video::CColor(0.5f,0.0f,0.5f), 0.0f, 0.0f);
arr[7] = scene::CVertex3d( util::CVector3d(-0.5f, -0.5f, -0.5f), util::CVector3d(-0.5f, -0.5f, -0.5f), 
		video::CColor(0.0f,0.5f,0.5f), 0.0f, 0.0f);

// these are the indexes for the triangles
util::array<unsigned int> index;
index.resize(36);
unsigned int temp[36] = { 1,0,2, 1,2,3, 3,2,4, 2,5,4, 0,1,7, 
0,7,6, 0,6,2, 6,5,2, 6,7,4, 6,4,5, 1,3,7, 7,3,4 };





// my OGL init function
// (The OGL function calls are at the bottom)
bool COpenGLDriver::initDriver(const video::dimension2d& screenSize)
{
	int nPixelFormat;

	static PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),	// size of the structure
		1,				// version. Always set to 1
		PFD_DRAW_TO_WINDOW |		// support window
		PFD_SUPPORT_OPENGL |		// support opengl
		PFD_DOUBLEBUFFER,		// support double buffering
		PFD_TYPE_RGBA,			// RGBA color mode
		32,				// 32-bit color mode
		0, 0, 0, 0, 0, 0,		// ignore color bits, not used
		0,				// no alpha buffer
		0,				// ignore shift bit
		0,				// no accumulation buffer
		0, 0, 0, 0,			// ignore accumulation bits
		16,				// 16-bit z-buffer size
		0,				// no stencil buffer
		0,				// no auxillary buffer
		PFD_MAIN_PLANE,			// main drawing plane
		0,				// reserved
		0, 0, 0				// layer masks ignored
	};


	// if get hdc fails
	if( !(hDC = GetDC(Window)) ){
		util::Message::print("COpenGLDriver::initDriver: Cannot create a GL device context.");
		return false;
	}

	// chose best matching pixel format, return index
	if( !(nPixelFormat=ChoosePixelFormat(hDC, &pfd)) ){
		util::Message::print("COpenGLDriver::initDriver: Cannot find a suitable pixel format.");
		return false;
	}

	// set pixel format to device context
	if( !SetPixelFormat(hDC, nPixelFormat, &pfd) ){
		util::Message::print("COpenGLDriver::initDriver: Cannot set the pixel format.");
		return false;
	}

	// create rendering context
	if( !(hRC=wglCreateContext(hDC)) ){
		util::Message::print("COpenGLDriver::initDriver: Cannot create a GL rendering context.");
		return false;
	}

	// activate rendering context
	if( !wglMakeCurrent(hDC, hRC) ){
		util::Message::print("COpenGLDriver::initDriver: Cannot activate GL rendering context.");
		return false;
	}


	glShadeModel(GL_SMOOTH);		// enable smooth shading

	glDisable(GL_LIGHTING);			// lighting disabled
	glFrontFace(GL_CW);			// clockwise polygons are out, I think ??? FIXME
	glEnable(GL_CULL_FACE);

	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// really nice perspective calculations

	glClearDepth(1.0f);			// set gl clear depth
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	glEnable(GL_TEXTURE_2D);		// enable 2D texturing

	util::Message::print("COpenGLDriver::COpenGLDriver: OpenGL was initialised successfully.");
	resizeWindow(screenSize);	// set the viewport and aspect ratios properly


	return true;	// init went well
}





/**
 * Load an bitmap file for use in openGL.
 * \param f A reference to a CReadFile object that is open to a bitmap file.
 * \param dim A reference to store the height and width of the bitmap
 * \param size A pointer to an unsigned int to store the size of the bitmap (in bytes)
 */
unsigned char* OGLLoadBitmapFile(io::CReadFile& f, video::dimension2d& dim, unsigned int* size)
{
	BITMAPFILEHEADER bitmapFileHeader;	// bitmap file header
	BITMAPINFOHEADER bitmapInfoHeader;	// bitmap info header
	unsigned char* bitmapImage;			// bitmap image data


	// check if the file is valid
	if( !f.isOpen() ){
		util::Message::print("OGLLoadBitmapFile: \"%s\" is not a valid filename.", f.getFilename() );
		f.closeFile();	// close the file
		return NULL;
	}

	// read the bitmap file header
	f.read(&bitmapFileHeader, sizeof(BITMAPFILEHEADER) );

	// verify that this is a bitmap by checking for the universal bitmap id
	if( bitmapFileHeader.bfType != 0x4d42 ){
		util::Message::print("OGLLoadBitmapFile: \"%s\" is not a valid bitmap.", f.getFilename() );
		f.closeFile();	// close the file
		return NULL;
	}

	// read the bitmap information header
	f.read(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER) );

	// assign the size
	dim.height = bitmapInfoHeader.biHeight;
	dim.width = bitmapInfoHeader.biWidth;


	// move the file pointer to the beginning of bitmap data
	f.seek( bitmapFileHeader.bfOffBits, SEEK_SET );

	// allocate enough memory for the bitmap image data
	bitmapImage = new unsigned char[bitmapInfoHeader.biSizeImage];

	// vefiy memory alloc
	if( !bitmapImage ){
		util::Message::print("OGLLoadBitmapFile: could not allocate enough memory for bitmap.");
		f.closeFile();	// close the file
		return NULL;
	}
	
	// read the texture data from file into the allocated mem
	f.read(bitmapImage, sizeof(bitmapInfoHeader.biSizeImage) );

	char tempRGB;
	// swap the R and B values to get RGB since the bitmap color format is in BGR
	for(int i=0; i < bitmapInfoHeader.biSizeImage; i+= 3 ){
		tempRGB = bitmapImage;
		bitmapImage = bitmapImage[i+2];
		bitmapImage[i+2] = tempRGB;
	}

	
	*size = bitmapInfoHeader.biSizeImage;	// the size (in bytes) of the image data
	return bitmapImage;						// return the image data
}




If anyone can help, maybe see something that I've missed, or messed up, your help is appreciated. [smile] [Edited by - Endar on October 16, 2005 8:46:10 AM]
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Endar
Endar
* bump *

I probably should have posted this about now, instead of 8 hours ago. [smile]
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Geocyte
Geocyte
you only need to call glTexParameteri and glTexImage2D once during initialisation when you first bind the image to GL. Subsequently you need only call glBindTexture() and pass in the int that identifies the texture to GL. Use glGenTextures() to generate unique texture IDs.
Geocyte Has Committed Suicide.
_the_phantom_
_the_phantom_
any chance of an example of showing HOW you use the code you've posted..?
markr
markr
You're calling glTexImage2d loads of times per frame - that's just plain wrong.

Load all your textures initially, by generating texture names with glGenTextures(), then calling glBindTexture() to select the texture, then glTextImage2d(...)

Do all this once at the program startup.

Thereafter, all you need to do is call glBindTexture() when you want to change texture - the texture will probably already be in vram.

Mark
Geocyte
Geocyte



Here is a bitmap loader which definitely works... you can use to see if yours is at fault. I found that .biSizeImage was giving me incorrect results for some reason so I did that calculation manually. Note that once you have given the image data to GL it loads it into its own space in memory leaving you to delete your copy of the image data to free up some memory.


inline bool LoadBMP(std::string filename, unsigned int ↦_id) {	BITMAPFILEHEADER bmp_file_header; // "BITMAPFILEHEADER" is a win32api type	BITMAPINFOHEADER bmp_info_header; // "BITMAPINFOHEADER" is a win32api type	unsigned char* image;	unsigned char tmp;	std::ifstream fin;	fin.open(&filename[0], std::ios::binary);	if (fin.fail()) return false;	fin.read( (char *) &bmp_file_header, sizeof(BITMAPFILEHEADER) );	if (bmp_file_header.bfType != 0x4D42) return false; // BITMAP_ID is 0x4D42	fin.read( (char *) &bmp_info_header, sizeof(BITMAPINFOHEADER) );//	image = new unsigned char[bmp_info_header.biSizeImage]; // something wrong with biSizeImage... do calculation manually	image = new unsigned char[bmp_info_header.biWidth*bmp_info_header.biHeight*3];		if (image == NULL) return false;	fin.read( (char *) image, bmp_info_header.biWidth*bmp_info_header.biHeight*3);	if (!image) return false;	//swap colour format from BGR to RGB	unsigned int i;	for(i = 0; i < bmp_info_header.biWidth*bmp_info_header.biHeight*3; i += 3)	{		tmp = image;		image = image[i+2];		image[i+2]=tmp;	}		glGenTextures(1, ↦_id);	glBindTexture(GL_TEXTURE_2D, map_id);	    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	gluBuild2DMipmaps(	GL_TEXTURE_2D,						3,						bmp_info_header.biWidth,						bmp_info_header.biHeight,						GL_RGB,						GL_UNSIGNED_BYTE,						image); 	delete[] image;	return true;}
Geocyte Has Committed Suicide.
Endar
Endar
Quote:
Original post by phantom
any chance of an example of showing HOW you use the code you've posted..?


There's always something that I forget.

/** * A class that represents a simple mesh loaded from a simple file format. * The vertices used will be identical to the scene::CVertex3d object. */class CSimpleMesh	:	public ISceneGraphNode{public:	/** 	 * Header for a simple non-animated model file	 * The format of the vertex in the file is identical to the scene::CVertex3d object.	 */	struct simple_mesh_header{		char s[5];			///< should be "model" when read from the file		unsigned int num_vertices;	///< The total number of vertices in the file		unsigned int num_tris;		///< The total number of triangles in the file		unsigned int offset_vertices;	///< The offset in bytes, into the file for the vertices		unsigned int offset_tris;	///< The offset in bytes, into the file for the triangles		unsigned int offset_end;	///< The offset in bytes, into the file to the end of the file	};	/**	 * The data format of each triangle in the file	 */	struct simple_mesh_triangle{		unsigned int index[3];	// 3 indices for each triangle	};private:	simple_mesh_header m_meshHeader;	///< The header for the simple mesh file	scene::CMesh m_mesh;				///< Holds an indexed mesh	video::ITexture* m_texture;			///< The texture for the meshpublic:	/**	 * Constructor	 * \param f A reference to a CReadFile object that is opened to the model file	 * \param smgr A pointer to the scene manager	 * \param tex_name A C-style string with the name of the texture file	 */	CSimpleMesh(scene::ISceneManager* smgr, io::CReadFile& f, char* tex_name)		:	ISceneGraphNode(smgr), m_texture(NULL)	{		f.read(&m_meshHeader, sizeof(simple_mesh_header) );				if( strncmp(m_meshHeader.s, "model", 5) != 0 ){	// if isn't a valid simple mesh file			util::Message::print("CSimpleMesh::CSimpleMesh: "			"file \"%s\" isn't a valid simple mesh model file.",			f.getFilename() );			return;		}		// load the file		load(f);				if( tex_name )	// if there is a filename, load the texture			m_texture = smgr->getVideoDriver()->createTexture(tex_name);			}	/**	 * Destructor	 */	virtual ~CSimpleMesh()	{		if( m_texture )			m_texture->drop();	}	/**	 * Load the model from file	 * \param f A reference to a CReadFile object that is opened to the model file	 */	void load(io::CReadFile& f)	{		// allocate enough memory for both the vertices and indices		m_mesh.getVertexList().resize(m_meshHeader.num_vertices);		m_mesh.getIndexList().resize(m_meshHeader.num_tris * 3);		f.seek(m_meshHeader.offset_vertices, SEEK_SET);	// seek to vertices		// read the vertices directly into the array - probably shouldn't be doing this		f.read(m_mesh.getVertexList().getPtr(), m_meshHeader.num_vertices * sizeof(CVertex3d) );		f.seek(m_meshHeader.offset_tris, SEEK_SET);		// seek to triangle indices		// read the indices directly into the array - probably shouldn't be doing this		f.read(m_mesh.getIndexList().getPtr(), m_meshHeader.num_tris * sizeof(simple_mesh_triangle) );			}	/**	 * Draw the mesh	 */	virtual void render()	{		if( m_texture )			// if a valid texture			m_texture->apply();	// apply the texture		m_sceneManager->getVideoDriver()->drawIndexedTriangleList(					m_mesh.getVertexList().getPtr(), 					m_mesh.getVertexList().getSize(),					(unsigned int*)m_mesh.getIndexList().getPtr(), 					m_mesh.getIndexList().getSize() );		ISceneGraphNode::render();	// render all the child nodes	}};/**  * Create an openGL texture * \param filename A C-style null-terminated string that holds the  *				   path and name of the texture file. * \return A pointer to an ITexture object. */video::ITexture* COpenGLDriver::createTexture(char* filename){	std::string ext;			// the filename extension	std::string fname(filename);		// the filename	unsigned int pos = fname.find_last_of(".");// find the start of the file extension	unsigned char* data = NULL;	// image data	unsigned int size;		// the size (in bytes) of the image	unsigned int texture;		// texture identifier	video::dimension2d d;		// the size of the image	io::CReadFile f(filename);	// open a file object	video::ITexture* texture_ptr = NULL;// temp pointer to a ITexture object	// error, no extension	if( pos == -1 ){				util::Message::print(		"COpenGLDriver::createTexture: could not find file extension.");		return NULL;	}	for(int i=pos; i < fname.size(); i++)		// copy the file extension		ext += fname;	glGenTextures( 1, &texture);		// generate 1 texture name and store in "array" of 1		// if a bitmap file	if( ext == ".bmp" ){		data = video::OGLLoadBitmapFile(f, d, &size);	// load the bitmap file and convert for OGL use		// if error loading image		if( !data ){			util::Message::print(			"COpenGLDriver::createTexture: error loading bitmap.");			glDeleteTextures(1, &texture);	// the generated name isn't used.			return NULL;		}		// create new OGL texture		texture_ptr = new video::COpenGLTexture(texture, d, size, data);		// error creating texture in memory		if( !texture_ptr ){			util::Message::print(			"COpenGLDriver::createTexture: error allocating "			"memory for bitmap texture.");			glDeleteTextures(1, &texture);	// the generated name isn't used.			return NULL;		}		delete data;		return texture_ptr;	// return the newly created texture	}	glDeleteTextures(1, &texture);	// the generated name isn't used.	// no loader for this file type	util::Message::print("COpenGLDriver::createTexture: "			"no loader for file type \"%s\"", ext.c_str() );	return NULL;}


There we go. That's it.

Note:: Because of the uncool way that line aren't wrapped between source tags, some of the params for the 'util::Message::print' function might look strange, without proper indentation, but it should still be correct, that is, if C++ still concatenates consecutive strings.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Endar
Endar
Quote:
Original post by Geocyte
Note that once you have given the image data to GL it loads it into its own space in memory leaving you to delete your copy of the image data to free up some memory.


So, the process goes something like:

1. Load bitmap from file and store
2. Generate OGL texture name
3. Bind bitmap data to texture name using:
// apply the textureglBindTexture(GL_TEXTURE_2D, m_texture);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (int)m_size.width, (int)m_size.height,			 0, GL_RGB, GL_UNSIGNED_BYTE, m_texData);

4. Delete stored bitmap data

Something like that? But then, how do I specify that I'm, using a particular texture? Just call glBindTexture again with the texture 'name'?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Geocyte
Geocyte
Quote:
Original post by Endar
Something like that? But then, how do I specify that I'm, using a particular texture? Just call glBindTexture again with the texture 'name'?


Yep :)

Geocyte Has Committed Suicide.
Endar
Endar
SCORE!!!!!

My major problem was I was loading 4 bytes from the bitmap file, instead of, you know, all the stuff I needed.

Now I just have to find out why it's backwards and upside down, and add some more vertices so I have the texture on each side of the cube, instead of wierdly stretched.

[smile]
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Nickolay_Smirnov
Nickolay_Smirnov
Why you don't use multiple levels of detail or at least GL_LINEAR filtration?
Cogito ergo sum (Descartes)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.