Search code examples
iosopengl-estexturesaugmented-reality

Loading image data into a texture using OpenGL-ES in Xcode (iOS) only works a certain way


I'm trying to load a texture that will be drawn on a simple square shape between four vertices. Unfortunately the only way this seems to work is if I put the code for loading the image data into the function that's called every frame. My code was originally based off an ARToolKit function (hence the drawCube name). Here is what my code looks like:

- (void) drawCube
{

glStateCacheEnableTex2D();
glStateCacheEnableBlend();
glStateCacheBlendFunc(GL_ONE, GL_SRC_COLOR);
glStateCacheEnableClientStateVertexArray();
glStateCacheEnableClientStateTexCoordArray();
glStateCacheEnableClientStateNormalArray();


const GLfloat cube_vertices [4][3] = {
    {-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}, {1.0f, -1.0f, -1.0f} };
const GLfloat texCoords [4][2] = {
    {0.0, 1.0}, {1.0, 1.0}, {0.0, 0.0}, {1.0, 0.0}
};
const GLfloat normals [4][3] = {
    {0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}
};


glGenTextures(1, &texture[0]);
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);

NSString *path = [[NSBundle mainBundle] pathForResource:@"iTunesArtwork" ofType:@"png"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil)
    NSLog(@"Do real error checking here");

GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef contextt = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGContextTranslateCTM (contextt, 0, height);
CGContextScaleCTM (contextt, 1.0, -1.0);
CGColorSpaceRelease( colorSpace );
CGContextClearRect(contextt, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM(contextt, 0, height - height );
CGContextDrawImage(contextt, CGRectMake( 0, 0, width, height ), image.CGImage );

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

CGContextRelease(contextt);

free(imageData);
[image release];
[texData release];

glPushMatrix(); // Save world coordinate system.
glScalef(20.0f, 20.0f, 20.0f);
glTranslatef(0.0f, 0.0f, 1.0f); // Place base of cube on marker surface.
glStateCacheDisableLighting();
glBindTexture(GL_TEXTURE_2D, texture[0]);
glStateCacheVertexPtr(3, GL_FLOAT, 0, cube_vertices);
glStateCacheNormalPtr(GL_FLOAT, 0, normals);
glStateCacheTexCoordPtr(2, GL_FLOAT, 0, texCoords);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glStateCacheDisableClientStateNormalArray();
glStateCacheDisableClientStateTexCoordArray();
glStateCacheDisableClientStateVertexArray();


glPopMatrix();    // Restore world coordinate system.
}

This code works fine, the only problem is it gets called every frame an AR marker is visible to the camera, which is of course not OK. However, when I try to move the image data lines to the init function, I only get a white square without the texture. The lines I moved were the top 3 Enable Tex2D/Blend and BlendFunc. Then after the normal coordinates, everything down to [texData release]. I've been basing my code off of a tutorial that I read here: http://iphonedevelopment.blogspot.com/2009/05/opengl-es-from-ground-up-part-6_25.html

Everything seems like it would be in the right place to me but clearly that's not the case. Could anyone please shed some light on my problem?


Solution

  • Not seeing your init code, I can't tell for sure. Possibly you're running this code to load a texture before setting up your OpenGL context. Context creation is done in the viewDidLoad method of the view controller in Apple's OpenGL app templates for Xcode. I wasn't able to download the template from this blog series, as the server is off line. Maybe your template creates the context in the same method. Try moving the texture loading code so it runs after the context creation.

    By the way, this tutorial is rather old. Since that blog entry was posted GLKit was added to the iOS SDK. GLKit includes very handy texture loading code.