Apr 012012
 

Prerequisites

This is the final part to a four part series about simulating a soft body physics in Box2d. Before continuing, you should read and understand the following tutorials.

1. 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 Part 2/4
4. Soft Body Physics with Box2d and Cocos2d Part 3/4

In this final part, I will be showing you how to texture map the circular polygon constructed from the coordinates of the Box2d wheel structure.

Texture Mapping a Circle


In the previous article, we texture mapped a square. Now, we will texture map a circle. In the image above, you see that the circle polygon is constructed with a triangle fan, with the center at 1, then going to 2, 3, 4, 5, 6, 7 and 9 (which is the same as 2). We need to map these vertices to the texture image. So we map 1 to A, 2 to B, 3 to C, 4 to D, 5 to E, 6 to F, 7 to G, 8 to H and 9 to I (etc, for however many number of vertices you have).

Just remember that the texture map coordinates must be in the range of 0 to 1. We use the cosine and sine functions to calculate the x and y coordinates of each segment. However, cosine and sine functions give outputs in the rage of -1 to 1. So we must normalize the cosine and sine functions so that the values fall between 0 to 1.

Here is the source code for MyNode. You will also find the full source code for download and a demo video at the bottom of the article.

#import "CCNode.h"
#import "Box2D.h"

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

static inline Vertex2D Vertex2DMake(GLfloat inX, GLfloat inY) {
    Vertex2D ret;
    ret.x = inX;
    ret.y = inY;
    return ret;
}

#define NUM_SEGMENTS 20

@interface MyNode : CCNode {
    // Texture
    CCTexture2D *texture;
    
    // Physics bodies
    NSMutableArray *bodies;
    float deltaAngle;
    
    // Center circle
    b2Body *innerCircleBody;
    
    // Polygon vertices
    Vertex2D triangleFanPos[NUM_SEGMENTS+2];
    
    // Texture coordinate array
    Vertex2D textCoords[NUM_SEGMENTS+2];
}

- (void) createPhysicsObject:(b2World*)world;
- (void) bounce;

@end
#import "cocos2d.h"
#import "MyNode.h"

#define PTM_RATIO 32.f

@implementation MyNode

- (id) init {
    self = [super init];

    texture = [[CCTextureCache sharedTextureCache] addImage:@"Ball.png"];        

    return self;
}

- (void) createPhysicsObject:(b2World *)world {
    // Center is the position of the circle that is in the center (inner circle)
    b2Vec2 center = b2Vec2(240/PTM_RATIO, 160/PTM_RATIO);
    b2CircleShape circleShape;
    circleShape.m_radius = 0.1f;
    
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &circleShape;
    fixtureDef.density = 0.1;
    fixtureDef.restitution = 0.05;
    fixtureDef.friction = 1.0;
    
    // Delta angle to step by
    deltaAngle = (2.f * M_PI) / NUM_SEGMENTS;
    
    // Radius of the wheel
    float radius = 50;

    // Need to store the bodies so that we can refer back
    // to it when we connect the joints
    bodies = [[NSMutableArray alloc] init];
    
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        // Current angle
        float theta = deltaAngle*i;
        
        // Calculate x and y based on theta
        float x = radius*cosf(theta);
        float y = radius*sinf(theta);
        // Remember to divide by PTM_RATIO to convert to Box2d coordinate
        b2Vec2 circlePosition = b2Vec2(x/PTM_RATIO, y/PTM_RATIO);
        
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        // Position should be relative to the center
        bodyDef.position = (center + circlePosition);
        
        // Create the body and fixture
        b2Body *body;
        body = world->CreateBody(&bodyDef);
        body->CreateFixture(&fixtureDef);
        
        // Add the body to the array to connect joints to it
        // later. b2Body is a C++ object, so must wrap it
        // in NSValue when inserting into it NSMutableArray
        [bodies addObject:[NSValue valueWithPointer:body]];
    }
    
    // Circle at the center (inner circle)
    b2BodyDef innerCircleBodyDef;
    // Make the inner circle larger
    circleShape.m_radius = 0.8f;
    innerCircleBodyDef.type = b2_dynamicBody;

    // Position is at the center
    innerCircleBodyDef.position = center;
    innerCircleBody = world->CreateBody(&innerCircleBodyDef);
    innerCircleBody->CreateFixture(&fixtureDef);
    
    // Connect the joints
    b2DistanceJointDef jointDef;
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        // The neighbor
        const int neighborIndex = (i + 1) % NUM_SEGMENTS;
        
        // Get current body and neighbor
        b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue];
        b2Body *neighborBody = (b2Body*)[[bodies objectAtIndex:neighborIndex] pointerValue];
        
        // Connect the outer circles to each other
        jointDef.Initialize(currentBody, neighborBody,
                            currentBody->GetWorldCenter(), 
                            neighborBody->GetWorldCenter() );
        // Specifies whether the two connected bodies should collide with each other
        jointDef.collideConnected = true;
        jointDef.frequencyHz = 0.0f;
        jointDef.dampingRatio = 0.0f;
        
        world->CreateJoint(&jointDef);
        
        // Connect the center circle with other circles        
        jointDef.Initialize(currentBody, innerCircleBody, currentBody->GetWorldCenter(), center);
        jointDef.collideConnected = true;
        jointDef.frequencyHz = 8.0;
        jointDef.dampingRatio = 0.5;
        
        world->CreateJoint(&jointDef);
    }
}



- (void) draw {
    // Using the wheel defined by the box2d objects, we'll be mapping a circle on
    // top of it using a triangle fan. First, we calculate the center. The center
    // needs to be mulitplied by the PTM_RATIO (to get the pixel coordinate from box2d coordinate)
    // and also must be offset by the current position (remember, in HelloWorldLayer, we set
    // the position to the center of the screen (myNode.position = ccp(240, 160).
    triangleFanPos[0] = Vertex2DMake(innerCircleBody->GetPosition().x * PTM_RATIO - self.position.x, 
                                     innerCircleBody->GetPosition().y * PTM_RATIO - self.position.y);
    // Use each box2d body as a vertex and calculate coordinate for the triangle fan
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue];
        Vertex2D pos = Vertex2DMake(currentBody->GetPosition().x * PTM_RATIO - self.position.x, 
                                    currentBody->GetPosition().y * PTM_RATIO - self.position.y);
        triangleFanPos[i+1] = Vertex2DMake(pos.x, pos.y);
    }
    // Loop back to close off the triangle fan
    triangleFanPos[NUM_SEGMENTS+1] = triangleFanPos[1];

    // The first vertex is the center of the triangle fan.
    // So the first coordinate of the texture map should
    // map to the center of the triangle fan. The texture
    // map coordinates are normalized between 0 and 1, so
    // the center is (0.5, 0.5)
    textCoords[0] = Vertex2DMake(0.5f, 0.5f);
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        
        GLfloat theta = M_PI + (deltaAngle * i);
        
        // Calculate the X and Y coordinates for texture mapping.
        // Need to normalize the cosine and sine functions so that the
        // values are between 0 and 1. Cosine and sine functions have
        // values from -1 to 1, so we divide by 2 and add 0.5 to it to
        // normalize the values between 0 and 1.
        textCoords[i+1] = Vertex2DMake(0.5+cosf(theta)*0.5, 
                                       0.5+sinf(theta)*0.5);

    }
    // Close it off.
    textCoords[NUM_SEGMENTS+1] = textCoords[1];
    
    // Enable texture mapping stuff
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    
    // Bind the OpenGL texture
    glBindTexture(GL_TEXTURE_2D, [texture name]);

    // Send the texture coordinates to OpenGL
    glTexCoordPointer(2, GL_FLOAT, 0, textCoords);
    // Send the polygon coordinates to OpenGL
    glVertexPointer(2, GL_FLOAT, 0, triangleFanPos);
    // Draw it 
    glDrawArrays(GL_TRIANGLE_FAN, 0, NUM_SEGMENTS+2);
    
    glEnableClientState(GL_COLOR_ARRAY);
}

- (void) bounce {
    b2Vec2 impulse = b2Vec2(innerCircleBody->GetMass() * 0, innerCircleBody->GetMass() * 40);
    b2Vec2 impulsePoint = innerCircleBody->GetPosition();
    innerCircleBody->ApplyLinearImpulse(impulse, impulsePoint);	          
}

@end

Source and Video

You can download the full source code here.

Soft Body Physics with Box2d and Cocos2d Part 1/4
Soft Body Physics with Box2d and Cocos2d Part 2/4
Soft Body Physics with Box2d and Cocos2d Part 3/4

 Posted by at 12:35 pm

  11 Responses to “Soft Body Physics With Box2d (and Cocos2d) Part 4/4”

  1. [...] Soft Body Physics with Box2d and Cocos2d Part 1/4 Soft Body Physics with Box2d and Cocos2d Part 2/4 Soft Body Physics with Box2d and Cocos2d Part 4/4 [...]

  2. Hi,Code is nice…But I want some modification in the code.I want this soft body effect physics,but I don’t want my image to rotate with body…I have a character which has legs and hands.So I want my character image straight standing always on jumping or after collision too…

    Will appreciate,if u can tell a way to solve this problem…

  3. nice code but how does this works for opengl 2.0

  4. Hello @Sebastian .

    did you solve it ? if you solved , Can you share me your code ?

    please Help

  5. Great tutorial. The end result is pretty impressive. I have one issue that I wasn’t able to resolve. Once the body starts moving, it doesn’t seem to be able to stop. It just slowly rolls left and right, and jitters a bit. How would I stop it from doing that?

    I’m trying to simulate a ball, and I increased the springness to about 50, to get a more firm body.

  6. very Good ! thank you for share

  7. Very Nice, thanks, can you provide the code of open es 2.0 version thanks

  8. [...] Nice sample project showing how to simulate Soft Body physics using box2d and cocos2d – LINK [...]

  9. I am using this soft body in my game. I got to knew loads of things from this tutorial, for that thanx.
    Now, i have one doubt about how to rotate the texture of the soft body when its steady at some position?

    Do you have ever try to implement this.?

    If you can help me on this, please do reply..!

    :)

  10. glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    i guess this is changed in openGL 2, what should I use if Im useing GL2.0 or higher ?

  11. Hi, can you convert your code into OpenGL ES 2.0? We can’t use it on new devices.

    regards,
    King

 Leave a Reply

(required)

(required)

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=""> <strike> <strong>