Mar 232012
 

Cocos2d and OpenGL

Cocos2d hides most of the complexities of OpenGL, but every now and then you will be forced to write some custom OpenGL code. You can override the draw method of CCNode to perform your custom OpenGL drawing. You don’t have to write the code for scaling, rotating or translating inside the draw method. Cocos2d will handle that for you. For example, if you manually draw a circle inside the draw method, you can move it with openGL’s glTranslate() call. However, a simpler way is to just reposition it by modifying the position property of the node:

mNode.position = ccp(100, 100)

If you need a primer on OpenGL, a good resource is Jeff Lamarche’s tutorials here.

Triangle Fan


For this quick tip, I will be drawing a circle using a triangle fan. Above is an image of a triangle fan. I’m going to be using a triangle fan to draw a circle on the screen.

It is pretty simple. First you define the origin, which is represented by 1 on the image. And then you define the rest of the vertices (2, 3, 4, 5, 6, 7) and then finally close it off by going back to 2. After you have defined all the vertices, you send it off to OpenGL to render it onto the screen.

Easy Math

I want to draw a circle, and I do not want to manually enter in all the vertices. So I will be generating the vertices in code. If you remember back to high school math, the x and y of a circle can be calculated with the use of the sine and cosine functions.

x = radius * cosine(theta)
y = radius * sine(theta)

Simple, right?

Code

First, create a Cocos2d project. Then create a class called MyNode, which inherits from CCNode. Inside HelloWorldLayer, create an instance of MyNode and add it to the scene like below. Notice that after the node has been added, I move it to position 200, 200 (I can do with OpenGL inside the draw method, but it’s faster and easier this way).

   
MyNode *n = [MyNode node];
n.position = ccp(200, 200);
[self addChild:n];

Here is the MyNode.h.

#import "CCNode.h"

typedef struct {
    GLfloat x;
    GLfloat y;
} Vertex2D;

static inline Vertex2D Vertex2DMake(GLfloat inX, CGFloat inY) {
    Vertex2D ret;
    ret.x = inX;
    ret.y = inY;
    return ret;
}
@interface MyNode : CCNode {
    Vertex2D *vertices;
    float radius;
    int numSegments;
}

And here is MyNode.m. I’ve commented the code, so it should be pretty easy to follow.

#import "MyNode.h"
#import "cocos2d.h"

@implementation MyNode

- (id) init {
    self = [super init];
    if (self) {
        // Radius of circle
        radius = 100;
        
        // Number segments of circle (bigger number, smoother circle)
        numSegments = 10;
        
        // Angle to step by
        float deltaAngle = 2 * M_PI / numSegments;
        
        // Allocate memory for vertices. We need 1 extra for the origin
        // and another for the end.
        vertices = (Vertex2D*) malloc(sizeof(Vertex2D) * (numSegments+1+2));
        
        // The origin vertex
        vertices[0] = Vertex2DMake(0, 0);
        
        for (int i = 1; i <= numSegments; i++) {
            GLfloat theta = deltaAngle*(i-1);
            
            // Calculate x and y based on theta
            GLfloat x = radius * cosf(theta);
            GLfloat y = radius * sinf(theta);
            
            vertices[i] = Vertex2DMake(x, y);
        }
        
        // Close it off
        vertices[numSegments+1] = vertices[1];
        
    }
    return self;

}



- (void) draw {
    [super draw];
    
    // Disable these states before drawing else you won't see anything
    glDisable(GL_TEXTURE_2D);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    
    // Set the color
    glColor4f(1.0, 0.0, 0.0, 1.0);
    
    // First argument - 2 numbers per Vertex2D (x and y)
    // Second argument - data type
    // Third argument - stride (ignore for now)
    // Fourth argument - pass the vertices
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    
    // Last argument - total number of vertices
    glDrawArrays(GL_TRIANGLE_FAN, 0, (numSegments+2));
    
    // Renable disabled states
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnable(GL_TEXTURE_2D);
}
@end

Source Code

You can find the source code for download here.

It is based on Cocos2d 1.0.1 on Mac OS X Lion and XCode 4.3.

 Posted by at 3:27 pm

  5 Responses to “Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan)”

  1. […] joints. The distance joints are configured to be springy. You may find it helpful to read through this post, which explains drawing a circle in Cocos2d using OpenGL. The math part will be relevant to our […]

  2. […] Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan) 2. Soft Body Physics with Box2d and Cocos2d Part […]

  3. […] Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan) 2. Soft Body Physics with Box2d and Cocos2d Part 1/4 3. Soft Body Physics with Box2d and Cocos2d […]

  4. […] Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan) 2. Soft Body Physics with Box2d and Cocos2d Part 1/4 3. Soft Body Physics with Box2d and Cocos2d […]

  5. IsnĀ“t there a memory-leak in the line:

    vertices = (Vertex2D*) malloc(sizeof(Vertex2D) * (numSegments+1+2));

    ?

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)