Thursday, October 9, 2008

Circles, Ellipses, and Regular Polygons in OpenGL ES

The iPhone's use of OpenGL ES means that a lot of games and other programs can be ported relatively quickly. Unfortunately, the iPhone uses a relatively old version of OpenGL ES (1.1), and by the very nature of an embedded graphic engine, a lot of the niceties of the expansive OpenGL have been removed, including functions to draw a lot of standard primitive shapes.

Lines and squares are pretty easy, of course, but what about more complex shapes? Well, you're on your own for those, so unless you're comfortable with doing a little math, you're going to have problems with OpenGL ES. Here's one example - how do you draw an ellipse or circle in OpenGL? I'm not talking about a sphere (we'll look at that in a later post, maybe), but a two-dimensional circle? Or a triangle?

These two functions draw (respectively) an ellipse and a circle (which is just an ellipse with equal height and width) at a given location, either filled, or outlined. But, they actually do more then that. You can specify how precise you want the ellipse to be using the segments argument. If you specify, three, for example, it will draw a three-sided polygon (aka a triangle). If you specify 360, you'll get a 360-sided regular polygon, which is going to look like a circle on all but the biggest displays. For most purposes on the iPhone, a value above 12 will look like a circle. A value less than 3 won't work, but the error checking is your responsibility. :)


void GLDrawEllipse (int segments, CGFloat width, CGFloat height, CGPoint center, bool filled)
{
glTranslatef(center.x, center.y, 0.0);
GLfloat vertices[segments*2];
int count=0;
for (GLfloat i = 0; i < 360.0f; i+=(360.0f/segments))
{
vertices[count++] = (cos(degreesToRadian(i))*width);
vertices[count++] = (sin(degreesToRadian(i))*height);
}
glVertexPointer (2, GL_FLOAT , 0, vertices);
glDrawArrays ((filled) ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, segments);
}
void GLDrawCircle (int circleSegments, CGFloat circleSize, CGPoint center, bool filled)
{
GLDrawEllipse(circleSegments, circleSize, circleSize, center, filled);
}




9 comments:

Alexi said...

Doesn't work.

Andrew said...

all works fine, thanks

Simon said...

First, thanks for the articles you've published. I'd like to know if Core Animation is powerful enough to be used for a 2D game with about 20-30 shape objects. BTW, i've added the circle function to an OpenGL project (in draw) and didn't see any results. Do you have any idea why? Is there any initialization needed?

Todd said...

Hi, I have the same problem, nothing shows up. Is there a trick to this? Thank you in advance! I've found all your blogs very very helpful and I've used your code in a couple of my projects.

Todd said...

I figured it out! This is probably working too hard, but I'm just a beginner at OpenGL coding. To make a simple circle anywhere on screen without the glTranslatef, do this:

This method:

GLDrawCircle(12, ballSize.x/320, ballPosition, true);

Calls this:



CGPoint middle = CGPointMake(320/2,480/2);

CGFloat xSeparation = 0;
int xDiff = 0;

if(middle.x > center.x){
xDiff = middle.x - center.x;
xDiff = -xDiff;
}

if(center.x > middle.x)
xDiff = center.x - middle.x;

xSeparation = xDiff / middle.x;

CGFloat ySeparation = 0;
int yDiff = 0;

if(middle.y > center.y){
yDiff = middle.y - center.y;
yDiff = -yDiff;
}

if(center.y > middle.y)
yDiff = center.y - middle.y;

ySeparation = yDiff / middle.y;

ySeparation = -ySeparation;

GLfloat vertices[segments*2];
int count=0;
for (GLfloat i = 0; i < 360.0f; i+=(360.0f/segments))
{
vertices[count++] = (cos(degreesToRadian(i))*width) + xSeparation;
vertices[count++] = (sin(degreesToRadian(i))*height) + ySeparation;

}

glVertexPointer (2, GL_FLOAT , 0, vertices);
glDrawArrays ((filled) ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, segments);



Now I'm trying to figure out how to color the whole thing correctly. I can color it sort of, by replacing the last two lines like this:

const GLubyte paddleColors[] = {
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255
};


glVertexPointer(2, GL_FLOAT, 0, vertices);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, paddleColors);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_FAN, 0, segments);


Any tips for getting the entire circle to color correctly?

Thanks!

lukewar said...

Many people may have problems with glTranslatef because you have to set some options before you use it.

first call:

glMatrixMode(GL_MODELVIEW);
this one makes that you will translate objects not the "world". to translate the world use GL_PROJECTION parameter.

and the code of the function slightly changed:

{
glLoadIdentity(); //here is the change. You call it to make OpenGL translate only once.
glTranslatef(center.x, center.y, -6.0);
GLfloat vertices[segments*2];
int count=0;
for (GLfloat i = 0; i < 360.0f; i+=(360.0f/segments))
{
vertices[count++] = (cos(3.14*i/180)*width);
vertices[count++] = (sin(3.14*i/180)*height);
}
glVertexPointer (2, GL_FLOAT , 0, vertices);
glDrawArrays ((filled) ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, segments);
}

Cheers.

Johan said...

Hi,

It's always disappointing to find out yet another basic OpenGL function is not included or limited in the ES version. Thanks for sharing the code, but...

I always try to avoid using cos/sin calcs because of CPU usage and it seems to me it would be wise to store the vertices in a custom object to prevent calling cos/sin every draw call. It increases mem usage slightly, or a lot if you have loads of different sizes but it might also be more efficient to glScale the circle (the geometry) since only the lines are drawn anyway (which won't scale up/get thicker/blury).

Perhaps I'm too new to it all to realize it's implied in your article and/or common(-sense) practice but wanted to mention it anyway. :)

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels