Click here to Skip to main content
15,886,519 members
Articles / Mobile Apps / iPhone

FreeType on OpenGL ES (iPhone)

Rate me:
Please Sign up or sign in to vote.
4.75/5 (10 votes)
24 Nov 2010CPOL4 min read 86.5K   3K   27  
Faster, smarter and better looking fonts rendered with OpenGL ES
#include "PreCompile.h"
#include "GLCallBatcher.h"
#include "ResManager.h"
#include "FTBitmapChar.h"

const int dataPerVextex = 5;
GLCallBatcher::GLCallBatcher()
{
	m_numQuads = 0;
	m_pVertexData = NULL;
	m_pIndices = NULL;
	m_textureID = 0;
	m_sizeCache = 0;
	m_drawCallCount = 0;
	// assume that we will have at least 150 quads strung together
	Reallocate(150);
}

GLCallBatcher::~GLCallBatcher()
{
	Release();
}

// deletes memory for arrays
void GLCallBatcher::Release()
{
	if (m_pVertexData != NULL)
	{
		delete[] m_pVertexData;
		m_pVertexData = NULL;
	}
	if (m_pIndices != NULL)
	{
		delete[] m_pIndices;
		m_pIndices = NULL;
	}
	m_sizeCache = 0;
}

// adds a quad of a give colour, alpha, etc
void GLCallBatcher::AddQuad(GLuint textureID, int color, float alpha, bool transparent, bool smooth,
	const float* pTexCoords, const float* pVertices) 
{
	SetAttribs(textureID, color, alpha, transparent, smooth);
	AddQuad(pTexCoords, pVertices);
}	// Render

// adds a quad and assumes that the colour, alpha is same as previous
void GLCallBatcher::AddQuad(const float* pTexCoords, const float* pVertices) 
{
	if (m_numQuads >= m_sizeCache)
	{
		Reallocate(20);
	}
	
	int currIndex = m_numQuads*verticesPerQuad*dataPerVextex;
	for (int n = 0; n < verticesPerQuad; n++)
	{
		// x,y,z: note: do not need to copy 3rd coordinate, just zero it
		m_pVertexData[currIndex+n*dataPerVextex] = pVertices[n*compVertPos];
		m_pVertexData[currIndex+n*dataPerVextex+1] = pVertices[n*compVertPos+1];
		// u,v
		m_pVertexData[currIndex+n*dataPerVextex+3] = pTexCoords[n*compVertTex];
		m_pVertexData[currIndex+n*dataPerVextex+4] = pTexCoords[n*compVertTex+1];
	}
	m_numQuads++;
}	// Render

// sets the current colour, alpha, etc,
// if this is different than previous then the current quads have to be rendered
// before adding any new ones
void GLCallBatcher::SetAttribs(GLuint textureID, int color, float alpha, bool transparent, bool smooth) 
{
	if (alpha > 1.0f) alpha = 1.0f;
	if (m_textureID != textureID || m_color != color || m_alpha != alpha ||
		m_transparent != transparent || m_smooth != smooth)
	{
		RenderCurr();
		m_textureID = textureID;
		m_color = color;
		m_alpha = alpha;
		m_transparent = transparent;
		m_smooth = smooth;
	}
}	// Render

// render the currently stored quads
void GLCallBatcher::RenderCurr()
{
	if (m_numQuads == 0) return;

	glDisable(GL_LIGHTING);
	glFrontFace(GL_CW);

	glEnable(GL_CULL_FACE);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	#ifdef LANDSCAPE 
	glRotatef(-90, 0, 0, 1);
	#endif
	// set orthographic, so that we are drawing billboard style using screen dimensions 
	glOrthof(0, (float)g_resManager.GetScreenWidth(), (float)g_resManager.GetScreenHeight(), 0, -50.0f, 50.0f);
	glActiveTexture(GL_TEXTURE1);
	glDisable(GL_TEXTURE_2D);
	glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);
	glClientActiveTexture(GL_TEXTURE1);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(GL_TEXTURE0);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisable(GL_DEPTH_TEST);	
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();

	if (m_transparent || m_alpha < 0.99f)
	{
		glEnable(GL_BLEND);	
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
	else
		glDisable(GL_BLEND);	
	glBindTexture(GL_TEXTURE_2D, m_textureID); 
	if (m_smooth)
	{
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
						GL_LINEAR);
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
						GL_LINEAR);
	}
	else
	{
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
						GL_NEAREST);
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
						GL_NEAREST);
	}
	if (m_alpha > 0.99f && m_color == WHITE)
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	}
	else
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
		const float inv256 = 1.0f/256.0f;
		float red = ((m_color>>16)&0xff)*inv256;
		float green = ((m_color>>8)&0xff)*inv256;
		float blue = (m_color&0xff)*inv256;
		if (m_alpha > 1.0f) m_alpha = 1.0f;
		glColor4f(red, green, blue, m_alpha);
	}
	/*
	 Set the vertex pointer.
	 param 1: Number of coordinates per vertex; must be 2, 3, or 4.
	 param 2: GL_FIXED for CommonLite and GL_FLOAT for Common profile.
	 param 3: Specifies the byte offset between consecutive vertexes. 
	 param 4: Offset to the start of the first vertex into the VBO.
	 */
	glVertexPointer(3, GL_FLOAT, sizeof(float)*dataPerVextex, m_pVertexData); 
	glTexCoordPointer(compVertTex, GL_FLOAT, sizeof(float)*dataPerVextex, 
					  &m_pVertexData[3]);
	assert(m_numQuads*indicesPerQuad < 0xffff);
    glDrawElements(GL_TRIANGLES, (GLsizei)(m_numQuads*indicesPerQuad), 
		GL_UNSIGNED_SHORT, m_pIndices);
	m_drawCallCount++;
	m_quadCount += m_numQuads;
	m_numQuads = 0;
	// revert back to previous state
    glMatrixMode(GL_PROJECTION);
	glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
#ifdef _DEBUG
	int err = glGetError();
	if (err != GL_NO_ERROR)
	{
		cout << "OpenGL error!!!" << endl;
	}
#endif
}

// increase the memory alloacation
// new memory is allocated and old memory is copied, then old memory is deleted
void GLCallBatcher::Reallocate(int increase)
{
	int newSize = m_numQuads+increase;
	float* pVertices = new float[newSize*verticesPerQuad*dataPerVextex];	// x,y,z
	memset(pVertices, 0, newSize*verticesPerQuad*dataPerVextex*sizeof(float));
	GLushort* pIndices = new GLushort[newSize*indicesPerQuad];   // 2 triangles per quad
	// indices never change
	for (int n = 0; n < newSize; n++)
	{
		pIndices[n*indicesPerQuad] = (GLshort)n*verticesPerQuad;
		pIndices[n*indicesPerQuad+1] = (GLshort)n*verticesPerQuad+1;
		pIndices[n*indicesPerQuad+2] = (GLshort)n*verticesPerQuad+2;
		pIndices[n*indicesPerQuad+3] = (GLshort)n*verticesPerQuad+1;
		pIndices[n*indicesPerQuad+4] = (GLshort)n*verticesPerQuad+3;
		pIndices[n*indicesPerQuad+5] = (GLshort)n*verticesPerQuad+2;
	}
	if (m_numQuads > 0)
	{
		int numVert = m_numQuads*verticesPerQuad;
		memcpy(pVertices, m_pVertexData, numVert*dataPerVextex*sizeof(float));
//		memcpy(pTexCoords, m_pTexCoords, numVert*compVertTex*sizeof(float));
		Release();
	}
	m_pVertexData = pVertices;
//	m_pTexCoords = pTexCoords;
	m_pIndices = pIndices;
	m_sizeCache = newSize;
}

// output the statistics
void GLCallBatcher::OutputData(ostringstream& os) 
{
	os << " draw calls: " << m_drawCallCount << " quads:" << m_quadCount;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Astronautz
Spain Spain
After working in the software industry for many years, I've started my own games company that specialises in strategy games for mobile platforms.

Comments and Discussions