Why won't my texture display?

Member
Posts: 48
Joined: 2011.03
Post: #1
Hi. I have some OpenGL code that displays characters stored within a TGA file on my PC and Mac which I have tried to get working with OpenGLES in the iPhone simulator. Instead of displaying a character, I just end up with an opaque square.

At first I thought it must be my TGA loading code thats not working but I have since confirmed with the debugger that the memory contents of the read file are the same as the hexdump of the TGA file on the Mac - so its loading OK I would say.

I'm running a 2D Ortho view - I can happily draw triangles, rectangles etc on the screen - I just can't get my font display function to work. Here's a stripped down version. Can anybody see whats wrong? Is there something special I need to do on the iPhone (and simulator) to get this to work?


Code:
glShadeModel( GL_SMOOTH );
  
  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );

  glClear( GL_COLOR_BUFFER_BIT );

...
    glMatrixMode( GL_PROJECTION );                        // Select The Projection Matrix
    glLoadIdentity();                            // Reset The Projection Matrix
  
  glOrthof( 0.0f, 640/*SCREEN_WIDTH*/, 960/*SCREEN_HEIGHT*/, 0.0f, -1.0f, 1.0f );
  
    glMatrixMode( GL_MODELVIEW );                        // Select The Modelview Matrix
    glLoadIdentity();                            // Reset The Modelview Matrix
  
  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
  
  glTranslatef(0.5, 0.5, 0);

...
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );            

  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
  glAlphaFunc( GL_GREATER, 0.1f );

  glEnable(GL_BLEND);
  glEnable(GL_ALPHA_TEST);
...

GLfloat *AddVertex3( GLfloat *pVertices, float fX, float fY )
{
  GLfloat *p = pVertices;
  
  *p = fX;   p ++;
  *p = fY;   p ++;
  *p = 0.0f; p ++;
  
  return p;
}

GLfloat *AddVertex2( GLfloat *pVertices, float fX, float fY )
{
  GLfloat *p = pVertices;
  
  *p = fX;   p ++;
  *p = fY;   p ++;
  
  return p;
}

void sprChar( int x, int y, char c )
{
  int i = c;

glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);

  glBindTexture( GL_TEXTURE_2D, Texture[ 0 ]);
  
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  
  glPushMatrix();
  
  float fSourceHeight     = 8.0f;
  float fSourceRowSpacing = 1.0f;
  float fCharsPerRow = 16.0f;
  float fCharsPerCol = 128.0f / ( fSourceRowSpacing + fSourceHeight );
  float fTexXStart = 0.0f;
  float fTexYStart = 0.0f;
  float fTexWidth  = 1.0f / fCharsPerRow;
  float fTexHeight = 1.0f / fCharsPerCol;
  
  float fOnePixelHeight = fTexHeight / g_fCharPixelHeight;
  float fOnePixelWidth  = fTexWidth  / g_fCharPixelWidth;
  
  float tX = g_fCharPixelWidth  / 2.0;
  float tY = g_fCharPixelHeight / 2.0;
  
  float xOffset = 0;
  float yOffset = 0;
  
  // Offset the drawing by half character width since all placement will be done from quad centre..
  xOffset = tX;
  yOffset = tY;
  
  fTexXStart = ( i % 16 ) * fTexWidth;
  fTexYStart = ( i / 16 ) * fTexHeight;
  
  glTranslatef( xOffset + x, yOffset + y, 0.0f);
  
  // Tighten up the source vertex so we don't pick up bits of the previous character..
  //
  // TODO: This should only be calculated once somewhere when switching fonts to speed things up!!
  fTexXStart = fTexXStart + ( fOnePixelWidth / 1.5f );
  fTexYStart = fTexYStart + ( fOnePixelHeight / 3.0f );

GLfloat srcVertices[ 100 ];
GLfloat dstVertices[ 100 ];
  GLfloat *pDst = dstVertices;
  GLfloat *pSrc = srcVertices;
  
  pDst = AddVertex3( pDst, -tX, -tY );
  pDst = AddVertex3( pDst,  tX, -tY );
  pDst = AddVertex3( pDst,  tX,  tY );
  
  pDst = AddVertex3( pDst, -tX, -tY );
  pDst = AddVertex3( pDst, -tX,  tY );
  pDst = AddVertex3( pDst,  tX,  tY );

  pSrc = AddVertex2( pSrc, fTexXStart, fTexYStart );
  pSrc = AddVertex2( pSrc, fTexXStart + fTexWidth, fTexYStart );
  pSrc = AddVertex2( pSrc, fTexXStart + fTexWidth, fTexYStart + fTexHeight );
  
  pSrc = AddVertex2( pSrc, fTexXStart, fTexYStart );
  pSrc = AddVertex2( pSrc, fTexXStart, fTexYStart + fTexHeight );
  pSrc = AddVertex2( pSrc, fTexXStart + fTexWidth, fTexYStart + fTexHeight );
  

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  
  glVertexPointer(   3, GL_FLOAT, 0, dstVertices );
  glTexCoordPointer( 2, GL_FLOAT, 0, srcVertices );
  glDrawArrays( GL_TRIANGLES, 0, 6 );
  
  glDisableClientState( GL_VERTEX_ARRAY );
  glDisableClientState( GL_TEXTURE_COORD_ARRAY );
  
  glPopMatrix();
  
  glBindTexture( GL_TEXTURE_2D, 0 );
}
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
You didn't paste the code that calls glGenTextures/glTexImage2D. Why are you calling glTextureParameteri each frame rather than when you create the texture object?
Quote this message in a reply
Member
Posts: 48
Joined: 2011.03
Post: #3
(Apr 7, 2011 01:40 PM)OneSadCookie Wrote:  You didn't paste the code that calls glGenTextures/glTexImage2D. Why are you calling glTextureParameteri each frame rather than when you create the texture object?

Hi Cookie. You'll noticed that some lines of code are not indented.. I became desperate trying to get this to work lastnight so I started copying and pasting GL function calls from other tutorials' examples etc. When using OpenGL on the Mac/PC, I never had the calls to glTextureParameter().

I also know there's massive room for optimisation in my code, but keep in mind that I'm still on the learning path and this code isn't for a cutting-edge game. :-)

The missing bits of code are below.. I can't remember where I grabbed the tga.cpp code from but its what I've been using - I see it makes the
glTextureParameter calls you mention.

I'd send you a complete Xcode project if I could but I'm at work at the moment.. and won't be home this evening.. I'd like to keep this open for other people to learn from, but PM me if you'd like me to ZIP up my project and send it to you when I can).

glGenTextures( NUM_TEXTURES, Texture );

LoadTexture("Paul1Fon8x9.tga",Texture[0]);

Code:
tga.cpp (Windows version copied below - I only changed the header files to get it working on the iPhone):

#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
//#include <windows.h>                            // Header File For Windows
#include <gl\gl.h>                                // Header File For The OpenGL32 Library
#include <gl\glu.h>                                // Header File For The GLu32 Library
#include <gl\glaux.h>                            // Header File For The GLaux Library
#include "tga.h"



TGAImg::TGAImg()
{
  pImage=pPalette=pData=NULL;
  iWidth=iHeight=iBPP=bEnc=0;
  lImageSize=0;
}


TGAImg::~TGAImg()
{
  if(pImage)
   {
    delete [] pImage;
    pImage=NULL;
   }

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

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


int TGAImg::Load(char* szFilename)
{
  using namespace std;
  ifstream fIn;
  unsigned long ulSize;
  int iRet;

  // Clear out any existing image and palette
   if(pImage)
    {
     delete [] pImage;
     pImage=NULL;
    }

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

  // Open the specified file
  fIn.open(szFilename,ios::binary);
    
   if(fIn==NULL)
    return IMG_ERR_NO_FILE;

  // Get file size
  fIn.seekg(0,ios_base::end);
  ulSize=fIn.tellg();
  fIn.seekg(0,ios_base::beg);

  // Allocate some space
  // Check and clear pDat, just in case
   if(pData)
    delete [] pData;

  pData=new unsigned char[ulSize];

   if(pData==NULL)
    {
     fIn.close();
     return IMG_ERR_MEM_FAIL;
    }

  // Read the file into memory
  fIn.read((char*)pData,ulSize);

  fIn.close();

  // Process the header
  iRet=ReadHeader();

   if(iRet!=IMG_OK)
    return iRet;

   switch(bEnc)
    {
     case 1: // Raw Indexed
      {
       // Check filesize against header values
        if((lImageSize+18+pData[0]+768)>ulSize)
         return IMG_ERR_BAD_FORMAT;

       // Double check image type field
        if(pData[1]!=1)
         return IMG_ERR_BAD_FORMAT;

       // Load image data
       iRet=LoadRawData();
        if(iRet!=IMG_OK)
         return iRet;

       // Load palette
       iRet=LoadTgaPalette();
        if(iRet!=IMG_OK)
         return iRet;

       break;
      }

     case 2: // Raw RGB
      {
       // Check filesize against header values
        if((lImageSize+18+pData[0])>ulSize)
         return IMG_ERR_BAD_FORMAT;

       // Double check image type field
        if(pData[1]!=0)
         return IMG_ERR_BAD_FORMAT;

       // Load image data
       iRet=LoadRawData();
        if(iRet!=IMG_OK)
         return iRet;

       BGRtoRGB(); // Convert to RGB
       break;
      }

     case 9: // RLE Indexed
      {
       // Double check image type field
        if(pData[1]!=1)
         return IMG_ERR_BAD_FORMAT;

       // Load image data
       iRet=LoadTgaRLEData();
        if(iRet!=IMG_OK)
         return iRet;

       // Load palette
       iRet=LoadTgaPalette();
        if(iRet!=IMG_OK)
         return iRet;

       break;
      }

     case 10: // RLE RGB
      {
       // Double check image type field
        if(pData[1]!=0)
         return IMG_ERR_BAD_FORMAT;

       // Load image data
       iRet=LoadTgaRLEData();
        if(iRet!=IMG_OK)
         return iRet;

       BGRtoRGB(); // Convert to RGB
       break;
      }

     default:
      return IMG_ERR_UNSUPPORTED;
    }

  // Check flip bit
   if((pData[17] & 0x20)==0)
     FlipImg();

  // Release file memory
  delete [] pData;
  pData=NULL;

  return IMG_OK;
}


int TGAImg::ReadHeader() // Examine the header and populate our class attributes
{
  short ColMapStart,ColMapLen;
  short x1,y1,x2,y2;

   if(pData==NULL)
    return IMG_ERR_NO_FILE;

   if(pData[1]>1)    // 0 (RGB) and 1 (Indexed) are the only types we know about
    return IMG_ERR_UNSUPPORTED;

   bEnc=pData[2];     // Encoding flag  1 = Raw indexed image
                      //                2 = Raw RGB
                      //                3 = Raw greyscale
                      //                9 = RLE indexed
                      //               10 = RLE RGB
                      //               11 = RLE greyscale
                      //               32 & 33 Other compression, indexed

    if(bEnc>11)       // We don't want 32 or 33
     return IMG_ERR_UNSUPPORTED;


  // Get palette info
  memcpy(&ColMapStart,&pData[3],2);
  memcpy(&ColMapLen,&pData[5],2);

  // Reject indexed images if not a VGA palette (256 entries with 24 bits per entry)
   if(pData[1]==1) // Indexed
    {
     if(ColMapStart!=0 || ColMapLen!=256 || pData[7]!=24)
      return IMG_ERR_UNSUPPORTED;
    }

  // Get image window and produce width & height values
  memcpy(&x1,&pData[8],2);
  memcpy(&y1,&pData[10],2);
  memcpy(&x2,&pData[12],2);
  memcpy(&y2,&pData[14],2);

  iWidth=(x2-x1);
  iHeight=(y2-y1);

   if(iWidth<1 || iHeight<1)
    return IMG_ERR_BAD_FORMAT;

  // Bits per Pixel
  iBPP=pData[16];

  // Check flip / interleave byte
   if(pData[17]>32) // Interleaved data
    return IMG_ERR_UNSUPPORTED;

  // Calculate image size
  lImageSize=(iWidth * iHeight * (iBPP/8));

  return IMG_OK;
}


int TGAImg::LoadRawData() // Load uncompressed image data
{
  short iOffset;

   if(pImage) // Clear old data if present
    delete [] pImage;

  pImage=new unsigned char[lImageSize];

   if(pImage==NULL)
    return IMG_ERR_MEM_FAIL;

  iOffset=pData[0]+18; // Add header to ident field size

   if(pData[1]==1) // Indexed images
    iOffset+=768;  // Add palette offset

   memcpy(pImage,&pData[iOffset],lImageSize);

  return IMG_OK;
}


int TGAImg::LoadTgaRLEData() // Load RLE compressed image data
{
  short iOffset,iPixelSize;
  unsigned char *pCur;
  unsigned long Index=0;
  unsigned char bLength,bLoop;

  // Calculate offset to image data
  iOffset=pData[0]+18;

  // Add palette offset for indexed images
   if(pData[1]==1)
    iOffset+=768;

  // Get pixel size in bytes
  iPixelSize=iBPP/8;

  // Set our pointer to the beginning of the image data
  pCur=&pData[iOffset];

  // Allocate space for the image data
   if(pImage!=NULL)
    delete [] pImage;

  pImage=new unsigned char[lImageSize];

   if(pImage==NULL)
    return IMG_ERR_MEM_FAIL;

  // Decode
   while(Index<lImageSize)
    {
      if(*pCur & 0x80) // Run length chunk (High bit = 1)
       {
        bLength=*pCur-127; // Get run length
        pCur++;            // Move to pixel data  

        // Repeat the next pixel bLength times
         for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize)
          memcpy(&pImage[Index],pCur,iPixelSize);
  
        pCur+=iPixelSize; // Move to the next descriptor chunk
       }
      else // Raw chunk
       {
        bLength=*pCur+1; // Get run length
        pCur++;          // Move to pixel data

        // Write the next bLength pixels directly
         for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize,pCur+=iPixelSize)
          memcpy(&pImage[Index],pCur,iPixelSize);
       }
    }

  return IMG_OK;
}


int TGAImg::LoadTgaPalette() // Load a 256 color palette
{
  unsigned char bTemp;
  short iIndex,iPalPtr;
  
   // Delete old palette if present
   if(pPalette)
    {
     delete [] pPalette;
     pPalette=NULL;
    }

  // Create space for new palette
  pPalette=new unsigned char[768];

   if(pPalette==NULL)
    return IMG_ERR_MEM_FAIL;

  // VGA palette is the 768 bytes following the header
  memcpy(pPalette,&pData[pData[0]+18],768);

  // Palette entries are BGR ordered so we have to convert to RGB
   for(iIndex=0,iPalPtr=0;iIndex!=256;++iIndex,iPalPtr+=3)
    {
     bTemp=pPalette[iPalPtr];               // Get Blue value
     pPalette[iPalPtr]=pPalette[iPalPtr+2]; // Copy Red to Blue
     pPalette[iPalPtr+2]=bTemp;             // Replace Blue at the end
    }

  return IMG_OK;
}


void TGAImg::BGRtoRGB() // Convert BGR to RGB (or back again)
{
  unsigned long Index,nPixels;
  unsigned char *bCur;
  unsigned char bTemp;
  short iPixelSize;

  // Set ptr to start of image
  bCur=pImage;

  // Calc number of pixels
  nPixels=iWidth*iHeight;

  // Get pixel size in bytes
  iPixelSize=iBPP/8;

   for(Index=0;Index!=nPixels;Index++)  // For each pixel
    {
     bTemp=*bCur;      // Get Blue value
     *bCur=*(bCur+2);  // Swap red value into first position
     *(bCur+2)=bTemp;  // Write back blue to last position

     bCur+=iPixelSize; // Jump to next pixel
    }

}


void TGAImg::FlipImg() // Flips the image vertically (Why store images upside down?)
{
  unsigned char bTemp;
  unsigned char *pLine1, *pLine2;
  int iLineLen,iIndex;

  iLineLen=iWidth*(iBPP/8);
  pLine1=pImage;
  pLine2=&pImage[iLineLen * (iHeight - 1)];

   for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
    {
     for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
      {
       bTemp=*pLine1;
       *pLine1=*pLine2;
       *pLine2=bTemp;      
      }
    }

}


int TGAImg::GetBPP()
{
  return iBPP;
}


int TGAImg::GetWidth()
{
  return iWidth;
}


int TGAImg::GetHeight()
{
  return iHeight;
}


unsigned char* TGAImg::GetImg()
{
  return pImage;
}


unsigned char* TGAImg::GetPalette()
{
  return pPalette;
}

bool LoadTexture(char *TexName, GLuint TexHandle)
{
  TGAImg Img;        // Image loader

  // Load our Texture
   if(Img.Load(TexName)!=IMG_OK)
   {
     LogDebugf( "Failed to load %s (1)", TexName );
     return false;
   }

  glBindTexture(GL_TEXTURE_2D,TexHandle); // Set our Tex handle as current

  // Create the texture
   if(Img.GetBPP()==24)
    glTexImage2D(GL_TEXTURE_2D,0,3,Img.GetWidth(),Img.GetHeight(),0,
                 GL_RGB,GL_UNSIGNED_BYTE,Img.GetImg());
   else
   if(Img.GetBPP()==32)
    glTexImage2D(GL_TEXTURE_2D,0,4,Img.GetWidth(),Img.GetHeight(),0,
                 GL_RGBA,GL_UNSIGNED_BYTE,Img.GetImg());
   else
   {
     LogDebugf( "Failed to load %s (2)", TexName );

     return false;
   }

  // Specify filtering and edge actions
  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,GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

  LogDebugf( "Loaded %s OK", TexName );
  return true;
}


tga.h

Code:
#ifndef _TGA_H
#define _TGA_H

// TGA Loader - 16/11/04 Codehead

#include <iostream>
#include <fstream>
#include <memory.h>

#define NUM_TEXTURES 128

#define IMG_OK              0x1
#define IMG_ERR_NO_FILE     0x2
#define IMG_ERR_MEM_FAIL    0x4
#define IMG_ERR_BAD_FORMAT  0x8
#define IMG_ERR_UNSUPPORTED 0x40

void LogDebugf( char *pchFormat, ... );
void LogDebug( char *s );


class TGAImg
{
  public:
   TGAImg();
   ~TGAImg();
   int Load(char* szFilename);
   int GetBPP();
   int GetWidth();
   int GetHeight();
   unsigned char* GetImg();       // Return a pointer to image data
   unsigned char* GetPalette();   // Return a pointer to VGA palette

  private:
   short int iWidth,iHeight,iBPP;
   unsigned long lImageSize;
   char bEnc;
   unsigned char *pImage, *pPalette, *pData;
  
   // Internal workers
   int ReadHeader();
   int LoadRawData();
   int LoadTgaRLEData();
   int LoadTgaPalette();
   void BGRtoRGB();
   void FlipImg();
};

#endif
Quote this message in a reply
Member
Posts: 48
Joined: 2011.03
Post: #4
Hi Cookie.

Here's a link to my Xcode4 project. You should be able to build the "Rectangles2" project for the iPhone simulator.

Xcode 4 Project

But here's the challenge - can you get the 'H' character to appear instead o the big white square? :-)

Cheers
Sparky
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #5
"3" and "4" are not legal internal formats for textures in OpenGL ES. Use GL_RGB and GL_RGBA instead.
Quote this message in a reply
Member
Posts: 48
Joined: 2011.03
Post: #6
(Apr 8, 2011 06:47 AM)OneSadCookie Wrote:  "3" and "4" are not legal internal formats for textures in OpenGL ES. Use GL_RGB and GL_RGBA instead.

Woohoo! You're the best! It works! :-) I changed both 3 & 4 values in the glTexImage2D() function to GL_RGBA and its working. I'll go read up on what the change actually did when I can face another night of programming. Now I can get on porting the rest of my program.. but by the looks of things its probably best I have a night off.

Thanks a lot, Cookie! Smile
Quote this message in a reply
Post Reply