Reputation: 176
I'm using OpenGL to create a simple 3D game, and I really don't understand why this error is ocurring. I'm trying to load a numberset (a set of files, named "1.tga", "2.tga", etc.) to use as the score display, but, for some reason, if any one of the images' dimensions is not a power of two, then I get the error:
"This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information."
Even stranger is that it doesn't count 128, 64, 32, etc. as being powers of two. What I don't understand is that in the program I have already loaded textures that are not powers of two (the skybox, which is 3072 x 2304), and that worked fine. But, for some reason, this doesn't.
I wasn't sure if giving my code would help, but here it is:
void CNumberSet::Load(char *NumberSetDirectory, bool BottomLeftAlign) {
std::string NSD;
m_BottomLeftAlign = BottomLeftAlign;
for (int i = 0; i < 11; i++) {
// Load texture for each digit
NSD = NumberSetDirectory;
if (NSD.at(NSD.length() - 1) == '\\' || NSD.at(NSD.length() - 1) == '/') NSD += (ToString(i) + ".tga");
else NSD += ("/" + ToString(i) + ".tga");
m_Textures[i].LoadDataFromFile((char *) NSD.c_str(), CTexture::TARGA);
m_Textures[i].SetTextureParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
m_Textures[i].SetTextureParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Load and create mesh for each digit, and associate with texture
float Width = m_Textures[i].GetWidth();
float Height = m_Textures[i].GetHeight();
if (BottomLeftAlign) {
float Vertices[] = {
0.0f, Height, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
Width, Height, 0.0f, 1.0f, 1.0f,
Width, 0.0f, 0.0f, 1.0f, 0.0f
};
m_Meshes[i].LoadData(Vertices, sizeof(Vertices), GL_STATIC_DRAW);
} else {
float Vertices[] = {
-Width, Height, 0.0f, 0.0f, 1.0f,
-Width, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, Height, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
m_Meshes[i].LoadData(Vertices, sizeof(Vertices), GL_STATIC_DRAW);
}
m_Meshes[i].SetTexture(&m_Textures[i]);
m_Meshes[i].SetVertexAttribute(m_ShaderProgram, "Vertex", 3, GL_FLOAT, 5 * sizeof(float), (void *)(0 * sizeof(float)));
m_Meshes[i].SetVertexAttribute(m_ShaderProgram, "TexCoord", 2, GL_FLOAT, 5 * sizeof(float), (void *)(3 * sizeof(float)));
}
}
The LoadDataFromFile
function is responsible for loading each image, and its source is here:
bool CTexture::LoadDataFromFile(char *Filename, short Type) {
std::ifstream File(Filename, std::ios::in | std::ios::binary);
switch (Type) {
case BMP24: {
char Header[54];
char *Data;
int Width, Height;
if (File.is_open()) {
File.read(Header, 54);
if (Header[0] != 'B' || Header[1] != 'M') {
MessageBox(NULL, "Not a valid BMP24 file!", "Error", MB_OK | MB_ICONERROR);
return false;
}
Width = *(int*) & Header[18];
Height = *(int*) & Header[22];
Data = new char [Width * Height * 3];
File.read(Data, Width * Height * 3);
File.close();
} else {
MessageBox(NULL, "Cannot open BMP24 file!", "Error", MB_OK | MB_ICONERROR);
return false;
}
glBindTexture(GL_TEXTURE_2D, m_ID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Width, Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, Data);
m_Width = Width;
m_Height = Height;
delete Data;
return true;
break; }
case TARGA: {
char Header[12];
char ImageInfo[6];
char *Data;
int Width, Height, BitDepth;
char UncompressedTGA[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
char CompressedTGA[12] = { 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (File.is_open()) {
File.read(Header, 12);
File.read(ImageInfo, 6);
Width = ImageInfo[1] * 256 + ImageInfo[0];
Height = ImageInfo[3] * 256 + ImageInfo[2];
BitDepth = ImageInfo[4];
int TGASize = Width * Height * BitDepth / 8;
Data = new char[TGASize];
m_Width = Width;
m_Height = Height;
if (BitDepth != 24 && BitDepth != 32) {
MessageBox(NULL, "Only bit depths of 24 and 32 are supported!", "Error", MB_OK | MB_ICONERROR);
return false;
}
if (memcmp(Header, UncompressedTGA, 12) == 0) {
// Uncompressed
File.read(Data, TGASize);
} else if (memcmp(Header, CompressedTGA, 12) == 0) {
// Compressed
unsigned char ChunkHeader = 0;
unsigned char *ColourBuffer = new unsigned char[BitDepth / 8];
unsigned int PixelCount = Width * Height;
unsigned int CurrentPixel = 0;
unsigned int CurrentByte = 0;
while (CurrentPixel < PixelCount) {
File.read((char *) &ChunkHeader, 1);
if (ChunkHeader < 128) { // RAW chunk
ChunkHeader++;
for (int i = 0; i < ChunkHeader; i++) {
File.read((char *)ColourBuffer, BitDepth / 8);
Data[CurrentByte + 0] = ColourBuffer[0]; // Write 'B' byte
Data[CurrentByte + 1] = ColourBuffer[1]; // Write 'G' byte
Data[CurrentByte + 2] = ColourBuffer[2]; // Write 'R' byte
if (BitDepth == 32) Data[CurrentByte + 3] = ColourBuffer[3]; // Write 'A' byte (if needed)
CurrentByte += BitDepth / 8;
CurrentPixel++;
}
} else { // Run-length encoded chunk
ChunkHeader -= 127;
File.read((char *) ColourBuffer, BitDepth / 8);
for (int i = 0; i < ChunkHeader; i++) {
Data[CurrentByte + 0] = ColourBuffer[0]; // Write 'B' byte
Data[CurrentByte + 1] = ColourBuffer[1]; // Write 'G' byte
Data[CurrentByte + 2] = ColourBuffer[2]; // Write 'R' byte
if (BitDepth == 32) Data[CurrentByte + 3] = ColourBuffer[3]; // Write 'A' byte (if needed)
CurrentByte += BitDepth / 8;
CurrentPixel++;
}
}
}
delete ColourBuffer;
} else {
MessageBox(NULL, "Not a valid TARGA file!", "Error", MB_OK | MB_ICONERROR);
return false;
}
File.close();
} else {
MessageBox(NULL, "Cannot open TARGA file!", "Error", MB_OK | MB_ICONERROR);
return false;
}
glBindTexture(GL_TEXTURE_2D, m_ID);
glTexImage2D(GL_TEXTURE_2D, 0, BitDepth == 24 ? GL_RGB : GL_RGBA, Width, Height, 0, BitDepth == 24 ? GL_BGR_EXT : GL_BGRA_EXT, GL_UNSIGNED_BYTE, Data);
delete Data;
return true;
break; }
default:
MessageBox(NULL, "Image format not recognised!", "Error", MB_OK | MB_ICONERROR);
return false;
break;
}
}
Upvotes: 0
Views: 427
Reputation: 54592
The constraint that gets in your way here is most likely not that the size needs to be a power of two, but that it needs to be a multiple of 4 with the default settings.
Note: This answer is similar to Missing Pixel When Loading Texture?, but I decided not to nominate the question as a duplicate because the problem scenario and the resulting symptoms are different.
The GL_UNPACK_ALIGNMENT
pixel storage value controls row alignment of the texture data passed to glTexImage2D()
. The default value is 4. This means that the row size in bytes is rounded up to the next multiple of 4 when glTexImage2D()
reads your input data.
Since you specify your textures in the GL_BGR_EXT
format, it has 3 bytes per pixel. So the row size in bytes is only a multiple of 4 if the width of the texture is also a multiple of 4.
To make this work without padding the texture data you pass to glTexImage2D()
, the row alignment value has to be changed to 1:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Upvotes: 3