I'm writing a native app that should only display a little triangle with a texture.
But unfortunately, it everytime only displays a white triangle.
My code is very simple.
First to load a tga Image:
static const GLenum gl_format[4] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
unsigned int LoadTGATextureFromFile(const char* filename)
{
unsigned int handle;
unsigned char hdr[18];
unsigned char file_id[256 + 1];
int file;
file = open(filename, O_RDONLY);
if(file < 0)
{
Log(LOGLEVEL_ERROR, APPNAME, "Error: Failed to open tga file '%s' for read ing\n", filename);
return 0;
}
if(read(file, hdr, 18) != 18 || read(file, file_id, hdr[0]) != hdr[0])
{
Log(LOGLEVEL_ERROR, APPNAME, "Error: Unexpected EOF while reading header of '%s'\n", filename);
close(file);
return 0;
}
file_id[hdr[0]] = 0;
if(hdr[1] != 0 || (hdr[2] != 2 && hdr[2] != 3) || (hdr[16] != 8 && hdr[16] != 16 && hdr[16] != 24 && hdr[16] != 32))
{
Log(LOGLEVEL_ERROR, APPNAME, "Error: File '%s' has invalid format\n", filename);
close(file);
return 0;
}
int width = *(short*)(hdr + 12);
int height = *(short*)(hdr + 14);
if((width & (width - 1)) != 0 || (height & (height - 1)) != 0)
{
Log(LOGLEVEL_ERROR, APPNAME, "Error: File '%s' has invalid resolution %dx%d\n", filename, width, height);
close(file);
return 0;
}
int components = hdr[16] / 8;
unsigned char* data = new unsigned char [width * height * components];
if (read(file, data, width * height * components) != width * height * components)
{
Log(LOGLEVEL_ERROR, APPNAME, "Error: Unexpected EOF while reading image data of '%s'\n", filename);
close(file);
return 0;
}
close(file);
char dummy;
if(read(file, &dummy, 1) == 1)
Log(LOGLEVEL_ERROR, APPNAME, "Warning: TGA file '%s' has overlength\n", filename);
switch (components - 1)
{
char tmp;
case 2:
for (int i = 0; i < width * height; i += 3)
{
tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
break;
case 3:
for (int i = 0; i < width * height; i += 4)
{
tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
break;
default:
break;
}
glGenTextures(1, &handle);
glBindTexture(GL_TEXTURE_2D, handle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
delete [] data;
Log(LOGLEVEL_ERROR, APPNAME, "'%s' successfully loaded [handle = %d, FILE_ID = \"%s\", width = %d, height = %d, depth = %d] :)\n", filename, handle, file_id, width, height, components * 8);
return handle;
}
Loading the texture:
int texture = LoadTextureFormFile("/sdcard/test.tga");
Then to draw:
float tricoords[6] = { 0.0, 0.0, 1.0, 1.0, 0.0, 1.0 };
float texcoords[6] = { 0.0, 0.0, 1.0, 1.0, 0.0, 1.0 };
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, tricoords);
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
I know, that this code isnt optimized, but its only for debugging.
The logcat of my app prints:
successfully loaded tga [handle = 1, FILE_ID = "", width = 64, height = 128, depth = 32] :)
But the texture stays white.
Just found the mistake, texture mipmapping was enabled for the loaded texture the mipmaps were never created.
Changing this line:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
to this will disable mipmaps for the texture.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
Related
I'm getting a heap corruption error whenever the following method is called:
bool TextureData::loadFromMemory(char* memData, int w, int h)
{
GLuint texId;
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texId);
glTextureId = static_cast<int>(texId);
glBindTexture(GL_TEXTURE_2D, texId); // Bind the texture
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
memWidth = width = w;
memHeight = height = h;
int newPad = 0;//(4 - (w*4)%4)==4 ? 0 : (4 - (w*4)%4);
int oldPad = 0;//(4 - (w*3)%4)==4 ? 0 : (4 - (w*3)%4);
size_t size = (width+newPad)*height * 4;
char* data = 0;
data = new char[size];
memcpy(data, memData, size * sizeof(char));
// Texture generation
gluBuild2DMipmaps(GL_TEXTURE_2D, 4, memWidth, memHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);
delete [] data;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return true;
}
Where the char* data array given to this method is generated like this:
int imageSize = width_ * height_ * 4;
data_ = new char[imageSize];
SDL_LockSurface(TextureImage);
for (Uint y = 0; y < height_; ++y)
{
for (Uint x = 0; x < width_; ++x)
{
size_t currPixel = (y * width_ + x) * 4;
SDL_Color rgb;
Uint32 data = getPixel(TextureImage, x, y);
SDL_GetRGB(data, TextureImage->format, &rgb.r, &rgb.g, &rgb.b);
data_[currPixel] = (char)rgb.r;
data_[currPixel + 1] = (char)rgb.g;
data_[currPixel + 2] = (char)rgb.b;
data_[currPixel + 3] = (char)255;
}
}
SDL_UnlockSurface(TextureImage);
SDL_FreeSurface(TextureImage);
I can't figure out why the loadFromMemory method is causing a heap corruption and need a review, if anyone has any ideas.
I am attempting to use OpenGL and SDL, using SDL_ttf to render text to a texture, but the code is rendering garbage.
My "Render to texture code":
GLuint textToTexture(std::string & text, TTF_Font* font, glm::vec4 textColour, glm::vec4 bgColour)
{
if (!TTF_WasInit())
{
if (TTF_Init() == -1)
exit(6);
}
SDL_Color colour = { (Uint8)(textColour.r*255), (Uint8)(textColour.g*255), (Uint8)(textColour.b*255), (Uint8)(textColour.a*255) };
SDL_Color bg = { (Uint8)(bgColour.r*255), (Uint8)(bgColour.g*255), (Uint8)(bgColour.b*255), (Uint8)(bgColour.a*255) };
SDL_Surface *stringImage = NULL;
stringImage = TTF_RenderText_Blended(font, text.c_str(), colour);
if (stringImage == NULL)
{
exit(5);
}
GLuint trueH = powerofTwo(stringImage->h);
GLuint trueW = powerofTwo(stringImage->w);
unsigned char* pixels = NULL;
GLuint w = stringImage->w;
GLuint h = stringImage->h;
GLuint colours = stringImage->format->BytesPerPixel;
pixels = padTexture((unsigned char*)stringImage->pixels, w, h, pixels, trueW, trueH, colours);
GLuint format, internalFormat;
if (colours == 4) {
if (stringImage->format->Rmask == 0x000000ff)
format = GL_RGBA;
else
format = GL_BGRA;
}
else {
// no alpha
if (stringImage->format->Rmask == 0x000000ff)
format = GL_RGB;
else
format = GL_BGR;
}
internalFormat = (colours == 4) ? GL_RGBA : GL_RGB;
GLuint texId = 0;
//GLuint texture;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueW, trueH, 0,format, GL_UNSIGNED_BYTE, pixels);
// SDL surface was used to generate the texture but is no longer
// required. Release it to free memory
SDL_FreeSurface(stringImage);
free(pixels)
return texId;
}
The code for computing the correct dimensions for padding:
int powerofTwo(int num)
{
if (num != 0)
{
num--;
num |= num >> 1; // Divide by 2^k for consecutive doublings of k up to 32,
num |= num >> 2; // and then or the results.
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
num++;
}
return num;
}
and finally, the code that copies the bytes to a texture of the correct dimensions:
unsigned char* padTexture(unsigned char * src, int srcW, int srcH, unsigned char * dest, int width, int height, int bpp)
{
dest = (unsigned char*)calloc(1, width*height*bpp);
for (int i = 0; i < srcH; i++)
{
memcpy(dest + (width*i*bpp),src + (srcW*i*bpp), srcW*bpp);
}
return dest;
}
The result of this code is as follows: [![Garbled Texture][1]][1]
I have confirmed and error checked that SDL_TTF is properly initialized elsewhere in the codebase, and that the font is also being loaded.
I have tested with three different ttf fonts, with the same results.
Also, if I use any other TTF_rendering function (Shaded, Solid etc), A solid quad is rendered, and the "colours" variable in the textToTexture function also ends up as 1.
Additional:
As I previously stated, I tested with three ttf fonts:
MavenPro-Regular,
HelveticaNeueLTStd-Th
and another I found off the internet.
I was trying to render the string "Select Scenario".
The pre padded image dimensions are 138x25 pixels.
The post padded image dimensions are 256x32 pixels.
Update 1:
After fixing the bpp issue the new texture is as follows:
This image changes everytime I run the program.
Update 2:
After fixing the additional spotted errors with padding the image, and setting the pixel data to the texture itself, when I use TTF_RenderText_Blended all I get is a black quad, and when I use TTF_RenderText_Shaded I get:
Update 3:
I used SDL_SaveBMP immedietly before calling the GL code and after calling SDL_RenderText_Blended, the result was a completely white image, (given which text colour).
When I do the same using TTF_RenderText_Solid, The saved image is as it should be, but is rendered by opengl like the images you see above.
SDL_TTF initialized fine, the fonts load without error, and the text rendering returns no errors, so I can't think what to do next.
Update 4:
I have since refactored all the ttf code into a single function and removed the padding code (as modern opengl doesn't seem to care about it). However, despite all project settings and code now being identical to a test project that is known to work on the same hardware, the problem persists.
GLuint textToTexture(const char * text, const char * font, glm::vec4 textColour, glm::vec4 bgColour, unsigned int & texID)
{
if (!TTF_WasInit()) {
if (TTF_Init() == -1)
exit(6);
}
SDL_Color colour = { (Uint8)(textColour.r * 255), (Uint8)(textColour.g * 255), (Uint8)(textColour.b * 255),(Uint8)(textColour.a * 255) };
SDL_Color bg = { (Uint8)(bgColour.r * 255), (Uint8)(bgColour.g * 255), (Uint8)(bgColour.b * 255),255 };
TTF_Font* fontObj = TTF_OpenFont(font, 24);
if (!fontObj)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"Texture Error",
"Cannot load font to create texture.",
NULL);
return 0;
}
SDL_Surface *image = NULL;
image = TTF_RenderText_Blended(fontObj, text, colour);
if (image == NULL)
{
exit(5);
//exitFatalError("String surface not created.");
std::cout << "String surface not created." << std::endl;
}
unsigned char* pixels = NULL;
GLuint w = image->w;
GLuint h = image->h;
GLuint colours = image->format->BytesPerPixel;
GLuint externalFormat, internalFormat;
SDL_PixelFormat *format = image->format;
if (colours == 4) {
if (image->format->Rmask == 0x000000ff)
externalFormat = GL_RGBA;
else
externalFormat = GL_BGRA;
}
else {
// no alpha
if (image->format->Rmask == 0x000000ff)
externalFormat = GL_RGB;
else
externalFormat = GL_BGR;
}
internalFormat = (colours == 4) ? GL_RGBA : GL_RGB;
GLuint texId = 0;
//GLuint texture;
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, image->pixels);
//glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueW, trueH, 0, externalFormat, GL_UNSIGNED_BYTE, pixels);
glGenerateMipmap(GL_TEXTURE_2D);
//// SDL surface was used to generate the texture but is no longer
//// required. Release it to free memory
SDL_FreeSurface(image);
TTF_CloseFont(fontObj);
return texID;
}
I have a workaround that saves the image to bmp, then reloads it and creates a texture, but only when I use TTF_RenderText_Shaded. If I use TTF_RenderText_Blended, I get an single colour image which corresponds to the text colour.
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueH, trueW, 0,format, GL_UNSIGNED_BYTE, pixels);
trueH and trueW order is reversed
memcpy(src + (srcW*i*bpp), dest + (width*i*bpp), srcW*bpp);
Source and destination order reversed.
dest = (unsigned char*)calloc(0, width*height*bpp);
0 elements of size width*height*bpp allocated, which is 0 bytes. Should be 1 instead of 0.
Here is a complete example:
#include <SDL2/SDL.h>
#include <GL/gl.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static unsigned char* padTexture(unsigned char * src, int srcW, int srcH, unsigned char * dest, int width, int height, int bpp, const SDL_Palette *palette)
{
int dst_bpp = (bpp == 1) ? 4 : bpp;
dest = (unsigned char*)calloc(1, width*height*dst_bpp);
if(bpp != 1) {
for (int i = 0; i < srcH; i++)
{
memcpy(dest + (width*i*bpp), src + (srcW*i*bpp), srcW*bpp);
}
} else {
/* indexed - read colours from palette */
for(int i = 0; i < srcH; i++) {
for(int j = 0; j < srcW; j++) {
memcpy(dest + (width*i+j)*dst_bpp,
&palette->colors[src[srcW*i+j]], sizeof(SDL_Color));
}
}
}
return dest;
}
static int powerofTwo(int num) {
if (num != 0)
{
num--;
num |= num >> 1; // Divide by 2^k for consecutive doublings of k up to 32,
num |= num >> 2; // and then or the results.
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
num++;
}
return num;
}
static GLuint textToTexture(const char *text, TTF_Font* font) {
if (!TTF_WasInit()) {
if (TTF_Init() == -1)
exit(6);
}
SDL_Color colour = { 255, 255, 255, 255 };
SDL_Color bg = { 0, 0, 0, 255 };
SDL_Surface *stringImage = NULL;
// stringImage = TTF_RenderText_Blended(font, text, colour);
stringImage = TTF_RenderText_Shaded(font, text, colour, bg);
if (stringImage == NULL) {
exit(5);
}
GLuint trueH = powerofTwo(stringImage->h);
GLuint trueW = powerofTwo(stringImage->w);
unsigned char* pixels = NULL;
GLuint w = stringImage->w;
GLuint h = stringImage->h;
GLuint colours = stringImage->format->BytesPerPixel;
pixels = padTexture((unsigned char*)stringImage->pixels, w, h, pixels, trueW, trueH,
colours, stringImage->format->palette);
GLuint format, internalFormat;
/* If indexed, want resulting image to be 32bit */
if(colours == 1) {
colours = 4;
}
if (colours == 4) {
if (stringImage->format->Rmask == 0x000000ff)
format = GL_RGBA;
else
format = GL_BGRA;
}
else {
// no alpha
if (stringImage->format->Rmask == 0x000000ff)
format = GL_RGB;
else
format = GL_BGR;
}
internalFormat = (colours == 4) ? GL_RGBA : GL_RGB;
GLuint texId = 0;
//GLuint texture;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueW, trueH, 0,format, GL_UNSIGNED_BYTE, pixels);
// SDL surface was used to generate the texture but is no longer
// required. Release it to free memory
SDL_FreeSurface(stringImage);
free(pixels);
return texId;
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
SDL_Window *window = SDL_CreateWindow("SDL2 Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 600, 400, SDL_WINDOW_OPENGL);
SDL_GLContext gl_ctx = SDL_GL_CreateContext(window);
TTF_Font *font = TTF_OpenFont(".fonts/tahoma.ttf", 16);
if(font) {
printf("font loaded\n");
textToTexture("Select Scenario", font);
TTF_CloseFont(font);
}
int quit = 0;
while(!quit) {
SDL_Event ev;
while(SDL_PollEvent(&ev)) {
if(ev.type == SDL_QUIT || ev.type == SDL_KEYUP) {
quit = 1;
}
}
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0, 1);
glVertex2f(-0.5, -0.5);
glTexCoord2f(0, 0);
glVertex2f(-0.5, 0.5);
glTexCoord2f(1, 0);
glVertex2f(0.5, 0.5);
glTexCoord2f(1, 1);
glVertex2f(0.5, -0.5);
glEnd();
glFlush();
SDL_GL_SwapWindow(window);
}
SDL_GL_DeleteContext(gl_ctx);
SDL_DestroyWindow(window);
TTF_Quit();
SDL_Quit();
return 0;
}
Almost exact duplicate question: OpenGL font rendering using Freetype2.
I am trying to render text in my OpenGL program using FreeType2 (2.5.3), based on this tutorial: http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02, using the same structs as Bentebent did in his question (above).
I am having the same problem he was having with rectangles showing up instead of legible characters (even using the correct GL_TEXTURE0 parameter for glActiveTexture())
For example, rendering green strings "Hello" and "World" below it gives me this:
As with Bentebent, using gDebugger I can see that my textures are generated fine. I have scoured google/stackoverflow, tried different parameters for glTexImage2D, different formulas for color in my fragment shader, etc, but no luck. Been stuck for a while now. Any help is appreciated.
Structs to create texture atlas:
//DrawTestOpenGLWnd.h
struct FontCharacter
{
float advanceX;
float advanceY;
float bitmapWidth;
float bitmapHeight;
float bitmapLeft;
float bitmapTop;
float uvOffsetX;
float uvOffsetY;
};
struct FontTextureAtlas
{
GLuint texture;
GLint textureUniform;
int width;
int height;
FontCharacter characters[128];
FontTextureAtlas(FT_Face face, int h, GLint tUniform)
{
FT_Set_Pixel_Sizes(face, 0, h);
FT_GlyphSlot glyphSlot = face->glyph;
int roww = 0;
int rowh = 0;
width = 0;
height = 0;
memset(characters, 0, sizeof(FontCharacter));
for (int i = 32; i < 128; i++)
{
if (FT_Load_Char(face, i, FT_LOAD_RENDER))
{
TRACE("Loading character %c failed\n", i);
continue;
}
if (roww + glyphSlot->bitmap.width + 1 >= MAX_WIDTH)
{
width = std::max(width, roww);
height += rowh;
roww = 0;
rowh = 0;
}
roww += glyphSlot->bitmap.width + 1;
rowh = std::max(rowh, glyphSlot->bitmap.rows);
}
width = std::max(width, roww);
height += rowh;
glGenTextures(1, &texture);
if (glGetError() != GL_NO_ERROR){
TRACE("glGenTextures failed\n");
}
glActiveTexture(GL_TEXTURE0);
if (glGetError() != GL_NO_ERROR){
TRACE("glActiveTexture failed\n");
}
glBindTexture(GL_TEXTURE_2D, texture);
if (glGetError() != GL_NO_ERROR){
TRACE("glBindTexture failed\n");
}
glUniform1i(tUniform, 0);
textureUniform = tUniform;
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexImage2D failed\n");
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (glGetError() != GL_NO_ERROR){
TRACE("glPixelStorei failed\n");
}
glPixelStorei(GL_PACK_ALIGNMENT, 1);
if (glGetError() != GL_NO_ERROR){
TRACE("glPixelStorei failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
int ox = 0;
int oy = 0;
rowh = 0;
for (int i = 32; i < 128; i++)
{
if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {
TRACE("Loading character %c failed\n", i);
continue;
}
if (ox + glyphSlot->bitmap.width + 1 >= MAX_WIDTH) {
oy += rowh;
rowh = 0;
ox = 0;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);
if (glGetError() != GL_NO_ERROR) {
TRACE("BORKED AGAIN\n");
}
characters[i].advanceX = glyphSlot->advance.x >> 6;
characters[i].advanceY = glyphSlot->advance.y >> 6;
characters[i].bitmapWidth = glyphSlot->bitmap.width;
characters[i].bitmapHeight = glyphSlot->bitmap.rows;
characters[i].bitmapLeft = glyphSlot->bitmap_left;
characters[i].bitmapTop = glyphSlot->bitmap_top;
characters[i].uvOffsetX = ox / (float)width;
characters[i].uvOffsetY = oy / (float)height;
rowh = std::max(rowh, glyphSlot->bitmap.rows);
ox += glyphSlot->bitmap.width + 1;
}
TRACE("Generated a %d x %d (%d kb) texture atlas.\n", width, height, width * height / 1024);
}
~FontTextureAtlas()
{
glDeleteTextures(1, &texture);
}
};
InitFreeType function:
void DrawTestOpenGLWnd::InitFreeType(char * strFontFilePath)
{
m_error = FT_Init_FreeType(&m_library);
if (m_error) {
TRACE("An error occurred during library initialization");
}
m_error = FT_New_Face(m_library, strFontFilePath, 0, &m_face);
if (m_error == FT_Err_Unknown_File_Format) {
TRACE("Font file could be opened and read, but it appears that its font format is unsupported");
}
else if (m_error) {
TRACE("Font file could not be opened or read. Or it's broken.");
}
m_program_text = LoadShaders("TextVertexShader.vertexshader", "TextFragmentShader.fragmentshader");
glUseProgram(m_program_text);
m_uniform_texture = glGetUniformLocation(m_program_text, "texture");
m_uniform_textColor = glGetUniformLocation(m_program_text, "textColor");
glGenVertexArrays(1, &vao_text);
glBindVertexArray(vao_text);
glGenBuffers(1, &vbo_text);
//glBindBuffer(GL_ARRAY_BUFFER, vbo_text);
a48 = new FontTextureAtlas(m_face, 48, m_uniform_texture);
a24 = new FontTextureAtlas(m_face, 24, m_uniform_texture);
a12 = new FontTextureAtlas(m_face, 12, m_uniform_texture);
}
RenderText function:
void DrawTestOpenGLWnd::RenderText(char * text, FontTextureAtlas * atlas, float x, float y, float sx, float sy)
{
glUseProgram(m_program_text);
const unsigned char* p;
std::vector<glm::vec4> coords;
int c = 0;
for (p = (const unsigned char*)text; *p; p++)
{
float x2 = x + atlas->characters[*p].bitmapLeft * sx;
float y2 = -y - atlas->characters[*p].bitmapTop * sy;
float w = atlas->characters[*p].bitmapWidth * sx;
float h = atlas->characters[*p].bitmapHeight * sy;
x += atlas->characters[*p].advanceX * sx;
y += atlas->characters[*p].advanceY * sy;
if (!w || !h)
continue;
coords.push_back(
glm::vec4(
x2,
-y2,
atlas->characters[*p].uvOffsetX,
atlas->characters[*p].uvOffsetY)
);
coords.push_back(
glm::vec4(
x2 + w,
-y2,
atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
atlas->characters[*p].uvOffsetY)
);
coords.push_back(
glm::vec4(
x2,
-y2 - h,
atlas->characters[*p].uvOffsetX,
atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
);
coords.push_back(
glm::vec4(
x2 + w,
-y2,
atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
atlas->characters[*p].uvOffsetY)
);
coords.push_back(
glm::vec4(
x2,
-y2 - h,
atlas->characters[*p].uvOffsetX,
atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
);
coords.push_back(
glm::vec4(
x2 + w,
-y2 - h,
atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
);
}
glBindVertexArray(vao_text);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0);
glUniform1i(atlas->textureUniform, 0);
glBindTexture(GL_TEXTURE_2D, atlas->texture);
GLfloat textColor[4] = {0.0, 1.0, 0.0, 0.}; //green
glUniform4fv(m_uniform_textColor, 1, textColor);
glBindBuffer(GL_ARRAY_BUFFER, vbo_text);
glBufferData(GL_ARRAY_BUFFER, coords.size() * sizeof(glm::vec4), coords.data(), GL_DYNAMIC_DRAW);
//Position
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void*)0);
glDrawArrays(GL_TRIANGLES, 0, coords.size());
glDisableVertexAttribArray(0);
glBindVertexArray(0);
glUseProgram(0);
}
TextVertexShader.vertexshader:
#version 330 core
layout(location = 0) in vec4 pos_uv;
out vec2 uv;
void main(void) {
gl_Position = vec4(pos_uv.xy, 0, 1);
uv = pos_uv.zw;
}
TextFragmentShader.fragmentshader:
#version 330 core
in vec2 uv;
uniform sampler2D texture;
uniform vec4 textColor;
out vec4 color;
void main(void) {
color = vec4(textColor.rgb, texture2D(texture, uv).a);
}
Your texture format is R8, so it contains R channel only. Your shader seams to use A channel.
Compare these 2 lines of code:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED,
GL_UNSIGNED_BYTE, 0);
...
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width,
glyphSlot->bitmap.rows, GL_RED, ...
vs
color = vec4(textColor.rgb, texture2D(texture, uv).a);
I'd suggest to change GL_R8 & GL_RED to GL_ALPHA8 & GL_ALPHA if your OpenGL < 3.0 or change texture2D(texture, uv).a to texture2D(texture, uv).r otherwise.
OP here. This is my working code, as per Anonymous' suggestion:
In struct FontCharacterAtlas:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED,
GL_UNSIGNED_BYTE, 0);
...
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows,
GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);
In TextFragmentShader.fragmentshader:
color = vec4(textColor.rgb, texture2D(texture, uv).r);
Text renders, albeit not very nicely (some characters' sides are slightly cut off, and when using a24 or a12 atlas the font is quite messy) but that's another story.
Almost exact duplicate question: OpenGL font rendering using Freetype2.
I am trying to render text in my OpenGL program using FreeType2 (2.5.3), based on this tutorial: http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02, using the same structs as Bentebent did in his question (above).
I am having the same problem he was having with rectangles showing up instead of legible characters (even using the correct GL_TEXTURE0 parameter for glActiveTexture())
For example, rendering green strings "Hello" and "World" below it gives me this:
As with Bentebent, using gDebugger I can see that my textures are generated fine. I have scoured google/stackoverflow, tried different parameters for glTexImage2D, different formulas for color in my fragment shader, etc, but no luck. Been stuck for a while now. Any help is appreciated.
Structs to create texture atlas:
//DrawTestOpenGLWnd.h
struct FontCharacter
{
float advanceX;
float advanceY;
float bitmapWidth;
float bitmapHeight;
float bitmapLeft;
float bitmapTop;
float uvOffsetX;
float uvOffsetY;
};
struct FontTextureAtlas
{
GLuint texture;
GLint textureUniform;
int width;
int height;
FontCharacter characters[128];
FontTextureAtlas(FT_Face face, int h, GLint tUniform)
{
FT_Set_Pixel_Sizes(face, 0, h);
FT_GlyphSlot glyphSlot = face->glyph;
int roww = 0;
int rowh = 0;
width = 0;
height = 0;
memset(characters, 0, sizeof(FontCharacter));
for (int i = 32; i < 128; i++)
{
if (FT_Load_Char(face, i, FT_LOAD_RENDER))
{
TRACE("Loading character %c failed\n", i);
continue;
}
if (roww + glyphSlot->bitmap.width + 1 >= MAX_WIDTH)
{
width = std::max(width, roww);
height += rowh;
roww = 0;
rowh = 0;
}
roww += glyphSlot->bitmap.width + 1;
rowh = std::max(rowh, glyphSlot->bitmap.rows);
}
width = std::max(width, roww);
height += rowh;
glGenTextures(1, &texture);
if (glGetError() != GL_NO_ERROR){
TRACE("glGenTextures failed\n");
}
glActiveTexture(GL_TEXTURE0);
if (glGetError() != GL_NO_ERROR){
TRACE("glActiveTexture failed\n");
}
glBindTexture(GL_TEXTURE_2D, texture);
if (glGetError() != GL_NO_ERROR){
TRACE("glBindTexture failed\n");
}
glUniform1i(tUniform, 0);
textureUniform = tUniform;
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexImage2D failed\n");
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (glGetError() != GL_NO_ERROR){
TRACE("glPixelStorei failed\n");
}
glPixelStorei(GL_PACK_ALIGNMENT, 1);
if (glGetError() != GL_NO_ERROR){
TRACE("glPixelStorei failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (glGetError() != GL_NO_ERROR){
TRACE("glTexParameteri failed\n");
}
int ox = 0;
int oy = 0;
rowh = 0;
for (int i = 32; i < 128; i++)
{
if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {
TRACE("Loading character %c failed\n", i);
continue;
}
if (ox + glyphSlot->bitmap.width + 1 >= MAX_WIDTH) {
oy += rowh;
rowh = 0;
ox = 0;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);
if (glGetError() != GL_NO_ERROR) {
TRACE("BORKED AGAIN\n");
}
characters[i].advanceX = glyphSlot->advance.x >> 6;
characters[i].advanceY = glyphSlot->advance.y >> 6;
characters[i].bitmapWidth = glyphSlot->bitmap.width;
characters[i].bitmapHeight = glyphSlot->bitmap.rows;
characters[i].bitmapLeft = glyphSlot->bitmap_left;
characters[i].bitmapTop = glyphSlot->bitmap_top;
characters[i].uvOffsetX = ox / (float)width;
characters[i].uvOffsetY = oy / (float)height;
rowh = std::max(rowh, glyphSlot->bitmap.rows);
ox += glyphSlot->bitmap.width + 1;
}
TRACE("Generated a %d x %d (%d kb) texture atlas.\n", width, height, width * height / 1024);
}
~FontTextureAtlas()
{
glDeleteTextures(1, &texture);
}
};
InitFreeType function:
void DrawTestOpenGLWnd::InitFreeType(char * strFontFilePath)
{
m_error = FT_Init_FreeType(&m_library);
if (m_error) {
TRACE("An error occurred during library initialization");
}
m_error = FT_New_Face(m_library, strFontFilePath, 0, &m_face);
if (m_error == FT_Err_Unknown_File_Format) {
TRACE("Font file could be opened and read, but it appears that its font format is unsupported");
}
else if (m_error) {
TRACE("Font file could not be opened or read. Or it's broken.");
}
m_program_text = LoadShaders("TextVertexShader.vertexshader", "TextFragmentShader.fragmentshader");
glUseProgram(m_program_text);
m_uniform_texture = glGetUniformLocation(m_program_text, "texture");
m_uniform_textColor = glGetUniformLocation(m_program_text, "textColor");
glGenVertexArrays(1, &vao_text);
glBindVertexArray(vao_text);
glGenBuffers(1, &vbo_text);
//glBindBuffer(GL_ARRAY_BUFFER, vbo_text);
a48 = new FontTextureAtlas(m_face, 48, m_uniform_texture);
a24 = new FontTextureAtlas(m_face, 24, m_uniform_texture);
a12 = new FontTextureAtlas(m_face, 12, m_uniform_texture);
}
RenderText function:
void DrawTestOpenGLWnd::RenderText(char * text, FontTextureAtlas * atlas, float x, float y, float sx, float sy)
{
glUseProgram(m_program_text);
const unsigned char* p;
std::vector<glm::vec4> coords;
int c = 0;
for (p = (const unsigned char*)text; *p; p++)
{
float x2 = x + atlas->characters[*p].bitmapLeft * sx;
float y2 = -y - atlas->characters[*p].bitmapTop * sy;
float w = atlas->characters[*p].bitmapWidth * sx;
float h = atlas->characters[*p].bitmapHeight * sy;
x += atlas->characters[*p].advanceX * sx;
y += atlas->characters[*p].advanceY * sy;
if (!w || !h)
continue;
coords.push_back(
glm::vec4(
x2,
-y2,
atlas->characters[*p].uvOffsetX,
atlas->characters[*p].uvOffsetY)
);
coords.push_back(
glm::vec4(
x2 + w,
-y2,
atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
atlas->characters[*p].uvOffsetY)
);
coords.push_back(
glm::vec4(
x2,
-y2 - h,
atlas->characters[*p].uvOffsetX,
atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
);
coords.push_back(
glm::vec4(
x2 + w,
-y2,
atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
atlas->characters[*p].uvOffsetY)
);
coords.push_back(
glm::vec4(
x2,
-y2 - h,
atlas->characters[*p].uvOffsetX,
atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
);
coords.push_back(
glm::vec4(
x2 + w,
-y2 - h,
atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
);
}
glBindVertexArray(vao_text);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0);
glUniform1i(atlas->textureUniform, 0);
glBindTexture(GL_TEXTURE_2D, atlas->texture);
GLfloat textColor[4] = {0.0, 1.0, 0.0, 0.}; //green
glUniform4fv(m_uniform_textColor, 1, textColor);
glBindBuffer(GL_ARRAY_BUFFER, vbo_text);
glBufferData(GL_ARRAY_BUFFER, coords.size() * sizeof(glm::vec4), coords.data(), GL_DYNAMIC_DRAW);
//Position
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void*)0);
glDrawArrays(GL_TRIANGLES, 0, coords.size());
glDisableVertexAttribArray(0);
glBindVertexArray(0);
glUseProgram(0);
}
TextVertexShader.vertexshader:
#version 330 core
layout(location = 0) in vec4 pos_uv;
out vec2 uv;
void main(void) {
gl_Position = vec4(pos_uv.xy, 0, 1);
uv = pos_uv.zw;
}
TextFragmentShader.fragmentshader:
#version 330 core
in vec2 uv;
uniform sampler2D texture;
uniform vec4 textColor;
out vec4 color;
void main(void) {
color = vec4(textColor.rgb, texture2D(texture, uv).a);
}
Your texture format is R8, so it contains R channel only. Your shader seams to use A channel.
Compare these 2 lines of code:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED,
GL_UNSIGNED_BYTE, 0);
...
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width,
glyphSlot->bitmap.rows, GL_RED, ...
vs
color = vec4(textColor.rgb, texture2D(texture, uv).a);
I'd suggest to change GL_R8 & GL_RED to GL_ALPHA8 & GL_ALPHA if your OpenGL < 3.0 or change texture2D(texture, uv).a to texture2D(texture, uv).r otherwise.
OP here. This is my working code, as per Anonymous' suggestion:
In struct FontCharacterAtlas:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED,
GL_UNSIGNED_BYTE, 0);
...
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows,
GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);
In TextFragmentShader.fragmentshader:
color = vec4(textColor.rgb, texture2D(texture, uv).r);
Text renders, albeit not very nicely (some characters' sides are slightly cut off, and when using a24 or a12 atlas the font is quite messy) but that's another story.
I am having trouble loading textures onto an array of type GLuints but the problem doesn't occur with loading a single texture. When I debug it, I see that textureIDs isn't assigned any values and has only 0s even after setTexture(...) is called
but the problem doesn't happen on a single texture loaded onto textureID. Either I'm missing something so obvious or my understanding of Opengl or C++ is lacking.
Relevant functions within this context
GLuint textureIDs[3];
GLuint textureID;
Ground Constructor
Ground::Ground(void) :
textureId(0),groundTextures()
{
setAllTexture();
}
Draw : ground function that draws.. well the ground
void Ground::draw()
{
glPushMatrix();
//Ground
glPushMatrix();
glTranslatef(0,-1,0); //all buildings and ground
//ground
glPushMatrix();
glTranslatef(-5,0,-20);
glScalef(40,0.2,40);
setTexture("Textures/rue2.bmp", textureId, true, true);
drawRectangle(1.0f);
glPopMatrix();
glPopMatrix();
}
settexture() sets textures
void Ground::setTexture(const char* textureName, GLuint& textId, bool stretchX, bool stretchZ)
{
if (textId == 0)
{
textId = (GLuint)createTexture(textureName);
}
if (textId != 0)
{
//enable texture coordinate generation
glEnable(GL_NORMALIZE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND); //Enable alpha blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //Set the blend function
glBindTexture(GL_TEXTURE_2D, textId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, stretchX ? GL_REPEAT : GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, stretchZ ? GL_REPEAT : GL_CLAMP);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
}
setAllTexture: loads textures onto the array of GLuints
int Ground::setAllTexture()
{
setTexture("Textures/asphalt.bmp",groundTextures[0],false, false );
//set up textures for all of them and bind them to ground textures.
glBindTexture(GL_TEXTURE_2D, groundTextures[0]);
setTexture("Textures/concreteFloor.bmp",groundTextures[1],false, false);
//set up textures for all of them and bind them to ground textures.
glBindTexture(GL_TEXTURE_2D, groundTextures[1]);
setTexture("Textures/dirtyGrass.bmp",groundTextures[2],false, false);
//set up textures for all of them and bind them to ground texture
glBindTexture(GL_TEXTURE_2D, groundTextures[2]);
for ( unsigned int i =0 ; i < 3 ; i ++ )
//check to see if all textures loaded properly
{
if ( groundTextures[i] == 0 )
return false;
}
return true;
//if you're here, every texture was loaded correctly.
}
ImageLoader: loads a 24bit RGB bitmap
Image* loadBMP(const char* filename) {
ifstream input;
input.open(filename, ifstream::binary);
assert(!input.fail() || !"Could not find file");
char buffer[2];
input.read(buffer, 2);
assert(buffer[0] == 'B' && buffer[1] == 'M' || !"Not a bitmap file");
input.ignore(8);
int dataOffset = readInt(input);
//Read the header
int headerSize = readInt(input);
int width;
int height;
switch(headerSize) {
case 40:
//V3
width = readInt(input);
height = readInt(input);
input.ignore(2);
assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
assert(readShort(input) == 0 || !"Image is compressed");
break;
case 12:
//OS/2 V1
width = readShort(input);
height = readShort(input);
input.ignore(2);
assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
break;
case 64:
//OS/2 V2
assert(!"Can't load OS/2 V2 bitmaps");
break;
case 108:
//Windows V4
assert(!"Can't load Windows V4 bitmaps");
break;
case 124:
//Windows V5
assert(!"Can't load Windows V5 bitmaps");
break;
default:
assert(!"Unknown bitmap format");
}
//Read the data
int bytesPerRow = ((width * 3 + 3) / 4) * 4 - (width * 3 % 4);
int size = bytesPerRow * height;
auto_array<char> pixels(new char[size]);
input.seekg(dataOffset, ios_base::beg);
input.read(pixels.get(), size);
//Get the data into the right format
auto_array<char> pixels2(new char[width * height * 3]);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
for(int c = 0; c < 3; c++) {
pixels2[3 * (width * y + x) + c] =
pixels[bytesPerRow * y + 3 * x + (2 - c)];
}
}
}
input.close();
return new Image(pixels2.release(), width, height);
}
createTexture: creates a texture and returns a GLuint
unsigned int createTexture( const char* imageName )
{
Image* image = loadBMP(imageName);
GLuint textureId = 0;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,image->width, image- >height,0,GL_RGB,GL_UNSIGNED_BYTE,image->pixels);
delete image;
return (unsigned int) textureId;
}