The code below is my experiment with creating a C++ class for generating and/or loading OpenGL textures for the iPhone (it’s also an experiment with posting mixed C++ and Objective-C code). Not all graphics will load. It will load the ship.png file from the Apple CrashLanding example.
No one codes in a vacuum. A lot of the code here is from bits I pulled together from Apple sample code as well as the following:
OpenGL(R) SuperBible: Comprehensive Tutorial and Reference (4th Edition)
OpenGL(R) Programming Guide: The Official Guide to Learning OpenGL(R), Version 2.1 (6th Edition) – the source of the checkerboard texture code.
http://discussions.apple.com/thread.jspa?messageID=8858657&tstart=0
http://www.idevgames.com/forum/showthread.php?t=16578
http://andreicostin.com – check out his video demo.
/* * mcaTexture.h * * Created by Mitchell Allen on 4/17/09. * Copyright 2009 __MyCompanyName__. All rights reserved. * */ #ifndef __MCA_TEXTURE__ #define __MCA_TEXTURE__ #ifdef TARGET_OS_IPHONE #import #import #endif class mcaTexture { protected: GLuint m_texture; // OpenGL name for the texture public: GLuint getTexture(); void initWithChecks(); void initFromImage( NSString *location); }; #endif
UPDATE: Since the two main methods below have a block of common code – I refactored it into another method. You can find the updated listing in my next post: Refactored Texture Class.
/* * mcaTexture.mm * mcaGL1 * * Created by Mitchell Allen on 4/24/09. * Copyright 2009 __MyCompanyName__. All rights reserved. * */ #include "mcaTexture.h" #define checkImageWidth 64 #define checkImageHeight 64 static GLubyte checkImage[ checkImageHeight][checkImageWidth ][ 4 ]; GLuint mcaTexture::getTexture() { return m_texture; } void mcaTexture::initWithChecks() { NSLog( @"START: mcaTexture::initWithChecks( '...' )" ); int i, j, c; for( i = 0; i < checkImageHeight; i++ ) { for( j = 0; j < checkImageWidth; j++ ) { c = ((((i&0x8)==0)^((j&0x8))==0))*255; checkImage[i][j][0] = (GLubyte) c; checkImage[i][j][1] = (GLubyte) c; checkImage[i][j][2] = (GLubyte) c; checkImage[i][j][3] = (GLubyte) 255; // For ghost effect ( 255 / 2 ) } } GLint saveName; // Now use OpenGL ES to generate a name for the texture. // Pass by reference so that our texture variable gets set. glGenTextures(1, &m_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName); // Bind the texture name. glBindTexture(GL_TEXTURE_2D, m_texture); 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_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); // Specify a 2D texture image, providing a pointer to the image data in memory glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage); // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, spriteData); // glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, spriteData); glBindTexture(GL_TEXTURE_2D, saveName); NSLog( @"END: mcaTexture::initWithChecks( '...' )" ); } void mcaTexture::initFromImage( NSString *location) { NSLog( @"START: mcaTexture::initFromImage( '...' )" ); // Creates a Core Graphics image from an image file using our location. CGImageRef spriteImage = [UIImage imageNamed:location].CGImage; // Get the width and height of the image. size_t w = CGImageGetWidth(spriteImage); size_t h = CGImageGetHeight(spriteImage); //TODO - resize the width and the height to the nearest power of 2. //Only create a sprite if we were able to properly load the CG image. if(spriteImage) { NSLog( @"... image loaded successfully ..." ); // Allocated memory needed for the bitmap context GLubyte *spriteData = (GLubyte *) malloc(w * h * 4); // Use the bitmap creation function provided by the Core Graphics framework. CGContextRef spriteContext = CGBitmapContextCreate(spriteData, w, h, 8, w * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); // After we create the context, we can draw the sprite image to the context. CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat)w, (CGFloat)h), spriteImage); // We don't need the context at this point, so we need to release it to avoid memory leaks. CGContextRelease(spriteContext); GLint saveName; // Now use OpenGL ES to generate a name for the texture. // Pass by reference so that our texture variable gets set. glGenTextures(1, &m_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName); // Bind the texture name. glBindTexture(GL_TEXTURE_2D, m_texture); 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_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); // Specify a 2D texture image, providing a pointer to the image data in memory glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, spriteData); // glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, spriteData); glBindTexture(GL_TEXTURE_2D, saveName); // glEnable(GL_LIGHTING); // Release the image data, which is now unused. free(spriteData); } GLenum errGL = glGetError(); NSLog( @"... glGetError = %1 ...", errGL ); NSLog( @"END: mcaTexture::initFromImage( '...' )" ); }
Sample Usage
The sample code below is incomplete for brevity. It’s just meant to give you a general idea of how to use the texture class.
/* * mcaPlane.mm * * Created by Mitchell Allen on 4/17/09. * Copyright 2009 __MyCompanyName__. All rights reserved. * */ #include "mcaPlane.h" void mcaPlane::init( GLfloat xpos, GLfloat ypos, GLfloat fSize ) { // ... } void mcaPlane::initWithTexture( GLfloat xpos, GLfloat ypos, GLfloat fSize, GLuint gTexture ) { init( xpos, ypos, fSize ); m_glTexture = gTexture; } void mcaPlane::paint() { if( ! isVisible() ) return; //Push the matrix so we can keep it as it was previously. glPushMatrix(); glMatrixMode(GL_MODELVIEW); //Enable 2D textures. glEnable(GL_TEXTURE_2D); //Bind this texture. glBindTexture(GL_TEXTURE_2D, m_glTexture ); // Set the texture parameters to use a minifying filter and a linear filer. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //Store the coordinates/dimensions from the rectangle. CGFloat w = m_planeSize; CGFloat h = m_planeSize; //Translate the OpenGL context to the center for rotation. glTranslatef( m_xpos + w/2, m_ypos +h/2, 0.0f); //Apply the rotation over the axis. glRotatef(m_spriteRotation, 1.0f, 0.0f, 0.0f); //Translate back to the top left corner for drawing. glTranslatef(-w/2, -h/2, 0.0f); //Draw to match const GLfloat planeVertices[] = { // Define the plane face -1.0, 1.0, 0.0, // top left -1.0, -1.0, 0.0, // bottom left 1.0, -1.0, 0.0, // bottom right 1.0, 1.0, 0.0, // top right }; const GLshort squareTextureCoords[] = { // Plane 0, 1, // top left 0, 0, // bottom left 1, 0, // bottom right 1, 1, // top right }; glScalef( m_planeSize, m_planeSize, m_planeSize ); glRotatef( m_xRot, 1.0f, 0.0f, 0.0f ); glRotatef( m_yRot, 0.0f, 1.0f, 0.0f ); glRotatef( m_zRot, 0.0f, 0.0f, 1.0f ); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindTexture(GL_TEXTURE_2D, m_glTexture ); glVertexPointer(3, GL_FLOAT, 0, planeVertices ); glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords ); // Draw the plane in white glColor4f(1.0, 1.0, 1.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); //Allow transparency and blending. glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //Restore the model view matrix to prevent contamination. glPopMatrix(); }
mcaTexture txChecks, txShip; NSLog( @"... initializing textures ..." ); txChecks.initWithChecks(); txShip.initFromImage( @"Ship.png" ); NSLog( @"... initializing objects ..." ); testPlane[ 0 ].initWithTexture( 0.0f, 0.0f, 100.f, txChecks.getTexture() ); testPlane[ 1 ].initWithTexture( 50.0f, 0.0f, 100.f, txChecks.getTexture() ); testPlane[ 2 ].initWithTexture( 0.0f, 0.0f, 125.f, txShip.getTexture() );
May 5th, 2009 at 12:48 pm
[...] Experimental Texture Class May [...]