May 04

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() );

Tags: ,

One Response to “Experimental Texture Class”

  1. Refactored Texture Class Says:

    [...] Experimental Texture Class May [...]

Leave a Reply