Sign in to follow this  
AndyEsser

Problem Loading TGA Files

Recommended Posts

Hey there, I'm slowing working my way through and porting my code from VB to C/C++ - Below is the code I use to load .tga files for textures. I'm not getting any error messages, but I'm not getting the output I want. I'm just getting an empty white cube. I've enabled texturing, bound the texture and set the co-ordinates correctly on the cube, so the problem must lie in this code. Many thanks.
[source lang=cpp]
#include <stdio.h>

GLuint textureName;

typedef struct tagTARGAFILEHEADER
{
	unsigned char imageIDLength;

	unsigned char colourMapType;
	unsigned char imageTypeCode;

	short int colourMapOrigin;
	short int colourMapLength;
	short int colourMapEntrySize;
	short int imageXOrigin;
	short int imageYOrigin;
	short int imageWidth;
	short int imageHeight;
	unsigned char bitCount;
	unsigned char imageDescriptor;
} TARGAFILEHEADER;

typedef struct
{
	unsigned char imageTypeCode;
	short int imageWidth;
	short int imageHeight;
	unsigned char bitCount;
	unsigned char *imageData;
} TGAFILE;


bool extractTexture(char* path, TGAFILE *tgaFile)
{
	FILE			*filePtr;
	unsigned char	ucharBad;
	short int		sintBad;
	long			imageSize;
	int				colourMode;

	filePtr = fopen(path,"rb");
	if(!filePtr)
	{
		writeLog("[Failed]\n",FALSE);
		return FALSE;
	}

	fread(&ucharBad,sizeof(unsigned char),1,filePtr);
	fread(&ucharBad,sizeof(unsigned char),1,filePtr);

	fread(&tgaFile->imageTypeCode,sizeof(unsigned char),1,filePtr);
	
	if ((tgaFile->imageTypeCode != 2) && (tgaFile->imageTypeCode != 3))
	{
		fclose(filePtr);
		writeLog("[Failed]\n",FALSE);
		return FALSE;
	}

	fread(&sintBad,sizeof(short int),1,filePtr);
	fread(&sintBad,sizeof(short int),1,filePtr);
	fread(&ucharBad,sizeof(unsigned char),1,filePtr);
	fread(&sintBad,sizeof(short int),1,filePtr);
	fread(&sintBad,sizeof(short int),1,filePtr);

	fread(&tgaFile->imageWidth,sizeof(short int),1,filePtr);
	fread(&tgaFile->imageHeight,sizeof(short int),1,filePtr);

	fread(&tgaFile->bitCount,sizeof(short int),1,filePtr);

	fread(&ucharBad,sizeof(unsigned char),1,filePtr);

	colourMode = tgaFile->bitCount /8;
	imageSize = tgaFile->imageWidth * tgaFile->imageHeight * colourMode;

	tgaFile->imageData = (unsigned char*)malloc(sizeof(unsigned char)*imageSize);
	fread(tgaFile->imageData,sizeof(unsigned char),imageSize,filePtr);

	fclose(filePtr);
	writeLog("[Done]\n",FALSE);
	return TRUE;
}

bool loadTexture(char* path)
{
	FILE *fp;
	if((fp=fopen(path,"r")) ==NULL)
	{
		writeLog("[Failed]\n",FALSE);
		return FALSE;
	}
	else
	{	
		TGAFILE *myTGA;
		myTGA = (TGAFILE*)malloc(sizeof(TGAFILE));
		extractTexture(path,myTGA);

		glGenTextures(1,&textureName);

		char buffer[200];
		sprintf(buffer,"Creating Texture(ID:%i)                           ",textureName);
		writeLog(buffer,TRUE);
		writeLog("[Done]\n",FALSE);

		glBindTexture(GL_TEXTURE_2D,textureName);
		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_BGR,myTGA->imageWidth,myTGA->imageHeight,0,GL_RGB,GL_UNSIGNED_BYTE,myTGA->imageData);

		return TRUE;
	}
	fclose(fp);
}

Share this post


Link to post
Share on other sites
I'm not 100% sure, if the bug is here, but this isn't 100% correct OpenGL code:
glTexImage2D(GL_TEXTURE_2D,0,GL_BGR,myTGA->imageWidth,myTGA->imageHeight,0,GL_RGB,
GL_UNSIGNED_BYTE,myTGA->imageData);
Third parameter - it has to be not GL_BGR, but 3 (or if you load texture with alpha channel, then it'd be 4 an seventh parameter would be GL_RGBA) - I'm using for TGA loading this
glTexImage2D(GL_TEXTURE_2D,0,3,myTGA->imageWidth,myTGA->imageHeight,0,GL_RGB,GL_UNSIGNED_BYTE,myTGA->imageData);

Share this post


Link to post
Share on other sites
if((myTGA->bitCount/8)==3)
{
glTexImage2D(GL_TEXTURE_2D,0,myTGA->bitCount/8,myTGA->imageWidth,myTGA->imageHeight,0,GL_BGR,GL_UNSIGNED_BYTE,myTGA->imageData);
} else {
glTexImage2D(GL_TEXTURE_2D,0,myTGA->bitCount/8,myTGA->imageWidth,myTGA->imageHeight,0,GL_BGRA,GL_UNSIGNED_BYTE,myTGA->imageData);
}

Ok, I replaced the line in the code with the ones above. I'm making some progress, I can sort of make out the image, but each row seems to be offset by a pixel to the row above and the image is black and white.

I've tried replacing GL_BGR & GL_BGRA with GL_RGB & GL_RGBA respectively, but I'm sure TGA data is stored in BGR format.

Any further suggestions?

Share this post


Link to post
Share on other sites
Just to clear up some misinformation about the third parameter. Passing numbers is the old way of doing it, where the number represents the number of color components. This is not the same as bitcount/8. In common cases, 24/8 (a 24 bit image) is 3, which conicides with the number of color componenets, being 3 for RGB.

This is, however, a conceptual error and the wrong way to determine the parameter. What if the RGB image has 16 bits per color component? 48/3 is 6, and your code fails, becuse the image doesn't have 6 color channels.

In "modern" OpenGL, with modern referring to OpenGL 1.1 or so, the parameter is the explicit format you want. GL_RGB for three-channel images, GL_RGBA for four-channel images and so on. Note that GL_BGR is not a valid format, as the purpose of the internal format is only to determine the number of color components, and what components are present. So for a 3-channel RGB-image, GL_RGB is the proper parameter. The third and second last parameter is repsonsible to decode the physical memory layout of the source data. For example, GL_BGR and GL_UNSIGNED_BYTE if the source image is BGR with 8 bit components.

Share this post


Link to post
Share on other sites
Hi there guys,

Well I've gotten it pretty much working, thanks to your help. I was being stupid and had the texture being 33x33 which would be the cause of the misformatting of the image and also the massive slowdown I also encountered(with it being NPOT).

However, although for the most part it is now working, the colour is still slightly wrong. Instead of showing a green logo it is displaying a blue logo. I can't seem to find what the problem is.

Share this post


Link to post
Share on other sites
As Brother Bob pointed out already GL_BGR_EXT is ok as a format parameter, not an internal format parameter.

So if you are loading TGA files the data should be stored as BGRA so you can use GL_BGRA_EXT if your Gfx supports it.


glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,myTGA->imageWidth,myTGA->imageHeight,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,myTGA->imageData);

Try that

Share this post


Link to post
Share on other sites
Quote:
Original post by MARS_999
As Brother Bob pointed out already GL_BGR_EXT is ok as a format parameter, not an internal format parameter.

So if you are loading TGA files the data should be stored as BGRA so you can use GL_BGRA_EXT if your Gfx supports it.

Since I've already mentioned "modern" OpenGL, I might aswell do it again. BGR formats was moved to the core in 1998 with OpenGL 1.2. Why still use the extension name of it?

Share this post


Link to post
Share on other sites
Quote:
Original post by steven katic
Quote:

Why still use the extension name of it?


'coz it works maybe.

Some of us are lazy and can't be bothered going thru the little pain
of finding/making the right/descent up-to-date header file as a replacement.

If you can't be bothered finding a new header once, then I don't see how you can stand having to type four more characters every time you need the constant. But if that is truely the reason, then here is is.

Share this post


Link to post
Share on other sites
I pulled this out of one of my old sources and may be or may not be of some help. This will load the Targa into a generic texture structure that I also use to load DDS, BMP, PCX, HDR, Etc. The NearestPowerX, NearestPowerY, and IsPowerOf2 are used in the loader to resize the texture to make it compatible with OpenGL (you can ignore these). Also, ignore the P25D in front of the data types (P25Dint is int, P25Duchar is unsigned char, etc.). This should also take care of the GL_RGB, GL_BGR, etc. as this code will convert it to always be RGB(A) format.

Texture Structure

struct P25DTEXTURE
{
P25Duchar* Data;
P25Dint Width;
P25Dint Height;
P25Dint Channels;
P25Dbool IsPowerOf2;
P25Dint MemorySize;
P25Dint BitsPerPixel;
P25Dint NearestPowerX;
P25Dint NearestPowerY;
};



Targa Loader

//------------------------------------------------------------------------------------------------
//--
//--ImportTexture(*)
//--Version: 1.0
//--
//------------------------------------------------------------------------------------------------
P25Dbool P25DTEXTURETGAIMPORT::ImportTexture(P25Dstring Filename, P25DTEXTURE* Texture)
{
P25Duchar BitDepth = 0;
P25Dushort Width = 0;
P25Dushort Height = 0;
P25Duchar Length = 0;
P25Dint Channels = 0;
P25Duchar Pixel = 0;
P25Duchar Type = 0;
P25Dint PixelID = 0;
//
if(Filename.AsChar() == NULL || Texture == NULL)
{
return P25Dfalse;
}
//
FILE* File = fopen(Filename.AsChar(), "rb");
if(File == NULL)
{
return P25Dfalse;
}
//
fread(&Length, sizeof(P25Duchar), 1, File);
fseek(File, 1, SEEK_CUR);
fread(&Type, sizeof(P25Duchar), 1, File);
fseek(File, 9, SEEK_CUR);
fread(&Width, sizeof(P25Dushort), 1, File);
fread(&Height, sizeof(P25Dushort), 1, File);
fread(&BitDepth, sizeof(P25Duchar), 1, File);
fseek(File, Length + 1, SEEK_CUR);
//
if(Type != 10)
{
if(BitDepth == 24 || BitDepth == 32)
{
Channels = BitDepth >> 3;
Texture->Data = new P25Duchar[Channels * Width * Height];
if(Texture->Data == NULL)
{
if(Texture)
{
delete Texture;
Texture = NULL;
}
//
fclose(File);
return P25Dfalse;
}
//
for(P25Dint Y = 0; Y < Height; Y++)
{
P25Duchar *Line = &(Texture->Data[Channels * Width * Y]);
fread(Line, Channels * Width, 1, File);
//
for(P25Dint PixelID = 0; PixelID < Channels * Width; PixelID += Channels)
{
Pixel = Line[PixelID];
Line[PixelID + 0] = Line[PixelID + 2];
Line[PixelID + 2] = Pixel;
}
}
}
else if(BitDepth == 16)
{
P25Dushort Pixels = 0;
P25Dint Red = 0,
Green = 0,
Blue = 0;
//
Channels = 3;
Texture->Data = new P25Duchar[Channels * Width * Height];
if(Texture->Data == NULL)
{
if(Texture)
{
delete Texture;
Texture = NULL;
}
//
fclose(File);
return P25Dfalse;
}
//
for(P25Dint PixelID = 0; PixelID < Width * Height; PixelID++)
{
fread(&Pixels, sizeof(P25Dushort), 1, File);
Blue = (Pixels & 0x1f) << 3;
Green = ((Pixels >> 5) & 0x1f) << 3;
Red = ((Pixels >> 10) & 0x1f) << 3;
//
Texture->Data[3 * PixelID + 0] = Red;
Texture->Data[3 * PixelID + 1] = Green;
Texture->Data[3 * PixelID + 2] = Blue;
}
}
else
{
return P25Dfalse;
}
}
else
{
P25Dint ColorsRead = 0;
P25Duchar ID = 0;
//
Channels = BitDepth >> 3;
Texture->Data = new P25Duchar[Channels * Width * Height];
if(Texture->Data == NULL)
{
if(Texture)
{
delete Texture;
Texture = NULL;
}
//
fclose(File);
return P25Dfalse;
}
//
P25Duchar* Colors = new P25Duchar[Channels];
if(Colors == NULL)
{
if(Texture->Data)
{
delete[] Texture->Data;
Texture->Data = NULL;
}
//
if(Texture)
{
delete Texture;
Texture = NULL;
}
//
fclose(File);
return P25Dfalse;
}

while(PixelID < Width * Height)
{
fread(&ID, sizeof(P25Duchar), 1, File);
if(ID <= 128)
{
ID++;
while(ID)
{
fread(Colors, sizeof(P25Duchar) * Channels, 1, File);
if(BitDepth == 16)
{
Texture->Data[ColorsRead + 0] = Colors[0];
Texture->Data[ColorsRead + 1] = Colors[1];
Texture->Data[ColorsRead + 2] = Colors[2];
}
else
{
Texture->Data[ColorsRead + 0] = Colors[2];
Texture->Data[ColorsRead + 1] = Colors[1];
Texture->Data[ColorsRead + 2] = Colors[0];
}
//
if(BitDepth == 32)
{
Texture->Data[ColorsRead + 3] = Colors[3];
}
//
ID--;
PixelID++;
ColorsRead += Channels;
}
}
else
{
ID -= 127;
fread(Colors, sizeof(P25Duchar) * Channels, 1, File);
while(ID)
{
if(BitDepth == 16)
{
Texture->Data[ColorsRead + 0] = Colors[0];
Texture->Data[ColorsRead + 1] = Colors[1];
Texture->Data[ColorsRead + 2] = Colors[2];
}
else
{
Texture->Data[ColorsRead + 0] = Colors[2];
Texture->Data[ColorsRead + 1] = Colors[1];
Texture->Data[ColorsRead + 2] = Colors[0];
}
//
if(BitDepth == 32)
{
Texture->Data[ColorsRead + 3] = Colors[3];
}
//
ID--;
PixelID++;
ColorsRead += Channels;
}
}
}

if(Colors)
{
delete[] Colors;
Colors = NULL;
}
}

fclose(File);
Texture->Width = Width;
Texture->Height = Height;
Texture->Channels = Channels;
Texture->BitsPerPixel = Texture->Channels * 8;
Texture->NearestPowerX = P25DMATH::NearestPowerOf2(Width);
Texture->NearestPowerY = P25DMATH::NearestPowerOf2(Height);
Texture->IsPowerOf2 = P25DMATH::IsPowerOf2EX(Width, Height);
Texture->MemorySize = Texture->Channels * Texture->Width * Texture->Height * sizeof(P25Duchar);
return P25Dtrue;
}


Share this post


Link to post
Share on other sites
Another thing to note is do not forget that glTexImage2D expects textures with the dimensions in the power of two (64,128,256,512,1024,2048,etc.). This is the reason I have the IsPowerOf2 in my code. If it is not to the power of two then I resize the texture to NearestPowerX and NearestPowerY.

If you want to get around that then use gluBuild2DMipmaps instead of glTexImage2D. gluBuild2DMipmaps will accept textures that are not power of two.

Share this post


Link to post
Share on other sites
Quote:
Original post by UltimaX
If you want to get around that then use gluBuild2DMipmaps instead of glTexImage2D. gluBuild2DMipmaps will accept textures that are not power of two.

gluBuild2DMipmaps will resize the texture to a power of two if necessary, it won't just pass the texture to OpenGL. This is a requirement from pre-OpenGL 2.0 behaviour. But I guess I can play my "modern OpenGL" card once more in this thread. Non-power of two textures have been in the core for well over three years now, not to mention support via extensions before that. Although in this case it can be an issue with implementation support, but saying that glTexImage expects powers of two has actually been wrong for the last few years.

Share this post


Link to post
Share on other sites
Quote:
Original post by Brother Bob
Quote:
Original post by UltimaX
If you want to get around that then use gluBuild2DMipmaps instead of glTexImage2D. gluBuild2DMipmaps will accept textures that are not power of two.

gluBuild2DMipmaps will resize the texture to a power of two if necessary, it won't just pass the texture to OpenGL. This is a requirement from pre-OpenGL 2.0 behaviour. But I guess I can play my "modern OpenGL" card once more in this thread. Non-power of two textures have been in the core for well over three years now, not to mention support via extensions before that. Although in this case it can be an issue with implementation support, but saying that glTexImage expects powers of two has actually been wrong for the last few years.


Thanks for the info. I wrote my loader quite a few years ago and have not really touched it since. I should go through and rewrite now knowing this.

Share this post


Link to post
Share on other sites
Quote:

Quote:Original post by steven katic

Quote:
Why still use the extension name of it?




'coz it works maybe.

Some of us are lazy and can't be bothered going thru the little pain
of finding/making the right/descent up-to-date header file as a replacement.


If you can't be bothered finding a new header once, then I don't see how you can stand having to type four more characters every time you need the constant. But if that is truely the reason, then here is is.


I digress:

I appreciate your point. I downloaded the header with the intent of doing the right thing. I may even go with something like glew if/when needed.

Using a gl feature as core instead of as an extension is still more
work: Either by using a library (like glew) to make life easier or checking versions available on the specific box the app is running on programatically (just to decide wether to invoke a feature as core or as an extension).

At times it is more simple to use a lower common denominator of
old core gl + detecting and using any extensions as needed (simple: either it is available or it aint irrespective of gl driver version; granted 3 or 4 lines of UNelegant/messy code for each feature; And when it gets "too messy" it's time for glew etc)

The other option:

Detecting the gl version; #defining it so the glext.h in turn defines appropriate features available as core or extension. Then, I still have to
decide wether to invoke the feature as core or extension depending on the
gl version available on the box at the time (since a feature's availabilty as core or extension depends in what gl version (of drivers) the feature happened to become core).

However, if your application is only targeted towards one computer [graphics card/driver(s)] and a specific gl version you would just define the version and be done with it.

But if you want to run your gl app on the broadest range of computers/gl driver versions(old and new), without it falling over you need to make sure the feature you are invoking is available beforehand. With regards to invoking it as a core feature or as an extension, I would have thought, is superfluous?.. unless coding aesthetics is important). Invoking a feature as an extension
means you can target a broader range of gl versions implicitly (i.e. no need for explicit code to account for a different version's feature as core or extension)

Maybe glew (type libs) do all that work for you?
e.g. vbo
install the library
invoke some form of glGenBuffers();
if it returns NULL/failure you know the feature is not available (simple)
as core or extension.
(Is that how it goes? I will take a look at some stage.)

Does that make sense?

Anyhow, sorry for the digession from the tga topic.

Late update FYI: I have had a look at Glee and Glew: using them makes sense.

[Edited by - steven katic on January 5, 2008 3:51:05 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by steven katic
Maybe glew (type libs) do all that work for you?

Only half of what you described, unfortunately.

While an arbitrary extension function glFooARB/EXT/WHATEVER is, most of the times, identical to the corresponding glFoo core function, there are cases where there are subtle behaviour changes made between the extension and the core. For example, some issue that turned up in the extension that was fixed when moved into the core. This makes merging of functions difficult, as most sane extension libraries automatically generates their source code from the extension registry. There are also, I believe, cases where the function behaves the same but the prototype is changed. All this would, in practice, require manual tweaking of automatically generated source code.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this