Skip to content

Instantly share code, notes, and snippets.

@slembcke
Created January 23, 2011 17:55
Show Gist options
  • Save slembcke/792276 to your computer and use it in GitHub Desktop.
Save slembcke/792276 to your computer and use it in GitHub Desktop.
/////// RopeNode.h
#import "cocos2d.h"
#import "ObjectiveChipmunk.h"
struct RopeNodeParticle;
@interface RopeNode : CCNode {
ChipmunkBody *_body1, *_body2;
cpVect _offset1, _offset2;
int _count;
cpFloat _segLength;
int _iterations;
cpFloat _width;
ccColor4B _color;
cpFloat _damping;
cpVect _gravity;
struct RopeNodeParticle *_particles;
}
@property(assign) cpFloat length;
@property(assign) cpFloat width;
@property(assign) ccColor4B color;
@property(assign) cpFloat damping;
@property(assign) cpVect gravity;
@property(assign) int iterations;
- (id)initWithLength:(cpFloat)length segments:(int)count
body1:(ChipmunkBody *)body1 body2:(ChipmunkBody *)body2
offset1:(cpVect)offset1 offset2:(cpVect)offset2;
- (void)step;
@end
/////// RopeNode.m
#import "RopeNode.h"
typedef struct RopeNodeParticle {
cpVect pos, prev;
} Particle;
@implementation RopeNode
@synthesize gravity = _gravity, iterations = _iterations, damping = _damping, width = _width, color = _color;
- (id)initWithLength:(cpFloat)length segments:(int)count
body1:(ChipmunkBody *)body1 body2:(ChipmunkBody *)body2
offset1:(cpVect)offset1 offset2:(cpVect)offset2;
{
if((self = [super init])){
_body1 = [body1 retain];
_body2 = [body2 retain];
_offset1 = offset1;
_offset2 = offset2;
_count = count;
_iterations = 5;
self.length = length;
_width = 1.0f;
_color = ccc4(255, 255, 255, 255);
cpVect start = [_body1 local2world:offset1];
cpVect end = [_body2 local2world:offset2];
_particles = calloc(count + 1, sizeof(Particle));
for(int i=0; i<=count; i++){
cpVect p = cpvlerp(start, end, (cpFloat)i/(cpFloat)count);
_particles[i] = (Particle){p, p};
}
}
return self;
}
- (void)dealloc {
[_body1 release];
[_body2 release];
free(_particles);
[super dealloc];
}
-(void)setLength:(cpFloat)length {
_segLength = length/_count;
}
-(cpFloat)length {
return _segLength;
}
static inline Particle
stepParticle(Particle p, cpVect g, cpFloat damping)
{
cpVect pos = p.pos;
cpVect vel = cpvmult(cpvsub(pos, p.prev), damping);
return (Particle){cpvadd(cpvadd(pos, vel), g), pos};
}
static inline void
contract(Particle *particles, int i, int j, cpFloat dSQ, cpFloat ratio)
{
cpVect v1 = particles[i].pos;
cpVect v2 = particles[j].pos;
cpVect delta = cpvsub(v2, v1);
if(cpvlengthsq(delta) < dSQ) return;
cpFloat diff = 0.5f - dSQ/(cpvlengthsq(delta) + dSQ);
particles[i].pos = cpvadd(v1, cpvmult(delta, diff*ratio));
particles[j].pos = cpvadd(v2, cpvmult(delta, diff*(ratio - 1.0)));
}
- (void)step;
{
int count = _count;
Particle *particles = _particles;
cpVect g = _gravity;
cpFloat damping = _damping;
for(int i=1; i<count; i++) particles[i] = stepParticle(particles[i], g, damping);
particles[0].pos = [_body1 local2world:_offset1];
particles[count].pos = [_body2 local2world:_offset2];
cpFloat dSQ = _segLength*_segLength;
for(int iteration=0; iteration<_iterations; iteration++){
contract(particles, 0, 1, dSQ, 0.0f);
for(int i=2; i<count; i++) contract(particles, i-1, i, dSQ, 0.5f);
contract(particles, count-1, count, dSQ, 1.0f);
}
}
static void
generateVertexes(Particle *particles, cpVect *verts, int count, cpFloat width)
{
cpVect v0 = particles[0].pos;
cpVect t0 = cpvperp(cpvnormalize_safe(cpvsub(particles[1].pos, v0)));
verts[0] = cpvadd(v0, cpvmult(t0, width));
verts[1] = cpvadd(v0, cpvmult(t0, -width));
for(int i=1; i<count-1; i++){
cpVect v1 = particles[i].pos;
cpVect t1 = cpvperp(cpvnormalize_safe(cpvsub(v1, v0)));
cpVect t = cpvmult(cpvnormalize_safe(cpvlerp(t0, t1, 0.5f)), width);
verts[2*i+0] = cpvadd(v1, t);
verts[2*i+1] = cpvadd(v1, cpvneg(t));
v0 = v1, t0 = t1;
}
cpVect v1 = particles[count-1].pos;
cpVect t1 = cpvperp(cpvnormalize_safe(cpvsub(v1, particles[count-2].pos)));
verts[2*count-2] = cpvadd(v1, cpvmult(t1, width));
verts[2*count-1] = cpvadd(v1, cpvmult(t1, -width));
}
- (void)draw {
int count = _count + 1;
cpVect verts[count*2];
generateVertexes(_particles, verts, count, _width);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// glBindTexture(GL_TEXTURE_2D, textureAtlas.name);
glDisable(GL_TEXTURE_2D);
glColor4ub(_color.r, _color.g, _color.b, _color.a);
glVertexPointer(2, GL_FLOAT, 0, verts);
glDrawArrays(GL_TRIANGLE_STRIP, 0, count*2);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
@end
@haqu
Copy link

haqu commented Apr 19, 2011

thanks! I'm going to try your code and see it in action.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment