cpVect cpBBClampVect(const cpBB bb, const cpVect v) { cpFloat x = cpfmin(cpfmax(bb.l, v.x), bb.r); cpFloat y = cpfmin(cpfmax(bb.b, v.y), bb.t); return cpv(x, y); }
static cpBB cpPolyShapeCacheData(cpShape *shape, cpVect p, cpVect rot) { cpPolyShape *poly = (cpPolyShape *)shape; cpFloat l, b, r, t; cpPolyShapeTransformAxes(poly, p, rot); cpPolyShapeTransformVerts(poly, p, rot); cpVect *verts = poly->tVerts; l = r = verts[0].x; b = t = verts[0].y; // TODO do as part of cpPolyShapeTransformVerts? for(int i=1; i<poly->numVerts; i++){ cpVect v = verts[i]; l = cpfmin(l, v.x); r = cpfmax(r, v.x); b = cpfmin(b, v.y); t = cpfmax(t, v.y); } return cpBBNew(l, b, r, t); }
static cpBB cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform) { int count = poly->count; struct cpSplittingPlane *dst = poly->planes; struct cpSplittingPlane *src = dst + count; cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY; cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY; for(int i=0; i<count; i++){ cpVect v = cpTransformPoint(transform, src[i].v0); cpVect n = cpTransformVect(transform, src[i].n); dst[i].v0 = v; dst[i].n = n; l = cpfmin(l, v.x); r = cpfmax(r, v.x); b = cpfmin(b, v.y); t = cpfmax(t, v.y); } cpFloat radius = poly->r; return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius)); }
static void applyImpulse(cpPulleyJoint *joint) { cpBody* b1 = joint->constraint.a; cpBody* b2 = joint->constraint.b; cpVect r1 = joint->r1; cpVect r2 = joint->r2; // The magic and mystery below if (joint->state) { cpVect v1 = cpvadd(b1->v, cpv(-b1->w * r1.y, b1->w * r1.x)); cpVect v2 = cpvadd(b2->v, cpv(-b2->w * r2.y, b2->w * r2.x)); cpFloat Cdot = -cpvdot(joint->u1, v1) - joint->ratio * cpvdot(joint->u2, v2); cpFloat impulse = joint->pulleyMass * (-Cdot); cpFloat oldImpulse = joint->jnAcc; joint->jnAcc = cpfmax(0.0f, joint->jnAcc + impulse); impulse = joint->jnAcc - oldImpulse; cpVect P1 = cpvmult(joint->u1, -impulse); cpVect P2 = cpvmult(joint->u2, -joint->ratio * impulse); cpBodyApplyImpulse(b1, P1, r1); cpBodyApplyImpulse(b2, P2, r2); } if (joint->limitState1) { cpVect v1 = cpvadd(b1->v, cpv(-b1->w * r1.y, b1->w * r1.x)); cpFloat Cdot = -cpvdot(joint->u1, v1); cpFloat impulse = -joint->limitMass1 * Cdot; cpFloat oldImpulse = joint->jnAccLim1; joint->jnAccLim1 = cpfmax(0.0f, joint->jnAccLim1 + impulse); impulse = joint->jnAccLim1 - oldImpulse; cpVect P1 = cpvmult(joint->u1, -impulse); cpBodyApplyImpulse(b1, P1, r1); } if (joint->limitState2) { cpVect v2 = cpvadd(b2->v, cpv(-b2->w * r2.y, b2->w * r2.x)); cpFloat Cdot = -cpvdot(joint->u2, v2); cpFloat impulse = -joint->limitMass2 * Cdot; cpFloat oldImpulse = joint->jnAccLim2; joint->jnAccLim2 = cpfmax(0.0f, joint->jnAccLim2 + impulse); impulse = joint->jnAccLim2 - oldImpulse; cpVect P2 = cpvmult(joint->u2, -impulse); cpBodyApplyImpulse(b2, P2, r2); } }
void cpArbiterApplyImpulse(cpArbiter *arb) { cpBody *a = arb->a->body; cpBody *b = arb->b->body; for(int i=0; i<arb->numContacts; i++){ cpContact *con = &arb->contacts[i]; cpVect n = con->n; cpVect r1 = con->r1; cpVect r2 = con->r2; // Calculate the relative bias velocities. cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias)); cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias)); cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n); // Calculate and clamp the bias impulse. cpFloat jbn = (con->bias - vbn)*con->nMass; cpFloat jbnOld = con->jBias; con->jBias = cpfmax(jbnOld + jbn, 0.0f); jbn = con->jBias - jbnOld; // Apply the bias impulse. cpVect jb = cpvmult(n, jbn); cpBodyApplyBiasImpulse(a, cpvneg(jb), r1); cpBodyApplyBiasImpulse(b, jb, r2); // Calculate the relative velocity. cpVect v1 = cpvadd(a->v, cpvmult(cpvperp(r1), a->w)); cpVect v2 = cpvadd(b->v, cpvmult(cpvperp(r2), b->w)); cpVect vr = cpvsub(v2, v1); cpFloat vrn = cpvdot(vr, n); // Calculate and clamp the normal impulse. cpFloat jn = -(con->bounce + vrn)*con->nMass; cpFloat jnOld = con->jnAcc; con->jnAcc = cpfmax(jnOld + jn, 0.0f); jn = con->jnAcc - jnOld; // Calculate the relative tangent velocity. cpVect t = cpvperp(n); cpFloat vrt = cpvdot(cpvadd(vr, arb->target_v), t); // Calculate and clamp the friction impulse. cpFloat jtMax = arb->u*con->jnAcc; cpFloat jt = -vrt*con->tMass; cpFloat jtOld = con->jtAcc; con->jtAcc = cpfmin(cpfmax(jtOld + jt, -jtMax), jtMax); jt = con->jtAcc - jtOld; // Apply the final impulse. cpVect j = cpvadd(cpvmult(n, jn), cpvmult(t, jt)); cpBodyApplyImpulse(a, cpvneg(j), r1); cpBodyApplyImpulse(b, j, r2); } }
void cpArbiterApplyImpulse(cpArbiter *arb, cpFloat eCoef) { cpBody *a = arb->a->body; cpBody *b = arb->b->body; for(int i=0; i<arb->numContacts; i++){ cpContact *con = &arb->contacts[i]; cpVect n = con->n; cpVect r1 = con->r1; cpVect r2 = con->r2; // Calculate the relative bias velocities. cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias)); cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias)); cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n); // Calculate and clamp the bias impulse. cpFloat jbn = (con->bias - vbn)*con->nMass; cpFloat jbnOld = con->jBias; con->jBias = cpfmax(jbnOld + jbn, 0.0f); jbn = con->jBias - jbnOld; // Apply the bias impulse. apply_bias_impulses(a, b, r1, r2, cpvmult(n, jbn)); // Calculate the relative velocity. cpVect vr = relative_velocity(a, b, r1, r2); cpFloat vrn = cpvdot(vr, n); // Calculate and clamp the normal impulse. cpFloat jn = -(con->bounce*eCoef + vrn)*con->nMass; cpFloat jnOld = con->jnAcc; con->jnAcc = cpfmax(jnOld + jn, 0.0f); jn = con->jnAcc - jnOld; // Calculate the relative tangent velocity. cpFloat vrt = cpvdot(cpvadd(vr, arb->surface_vr), cpvperp(n)); // Calculate and clamp the friction impulse. cpFloat jtMax = arb->u*con->jnAcc; cpFloat jt = -vrt*con->tMass; cpFloat jtOld = con->jtAcc; con->jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax); jt = con->jtAcc - jtOld; // Apply the final impulse. apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(jn, jt))); } }
static void NodeRender(Node *node, int depth) { if(!NodeIsLeaf(node) && depth <= 10){ NodeRender(node->a, depth + 1); NodeRender(node->b, depth + 1); } cpBB bb = node->bb; // GLfloat v = depth/2.0f; // glColor3f(1.0f - v, v, 0.0f); glLineWidth(cpfmax(5.0f - depth, 1.0f)); glBegin(GL_LINES); { glVertex2f(bb.l, bb.b); glVertex2f(bb.l, bb.t); glVertex2f(bb.l, bb.t); glVertex2f(bb.r, bb.t); glVertex2f(bb.r, bb.t); glVertex2f(bb.r, bb.b); glVertex2f(bb.r, bb.b); glVertex2f(bb.l, bb.b); }; glEnd(); }
static void playerUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) { cpBodyUpdateVelocity(body, gravity, damping, dt); body->v.y = cpfmax(body->v.y, -700); body->v.x = cpfclamp(body->v.x, -400, 400); }
void cpSpaceNearestPointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpLayers layers, cpGroup group, cpSpaceNearestPointQueryFunc func, void *data) { struct NearestPointQueryContext context = {point, maxDistance, layers, group, func}; cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); cpSpaceLock(space); { cpSpatialIndexQuery(space->activeShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data); cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data); } cpSpaceUnlock(space, cpTrue); }
static cpBB cpPolyShapeTransformVerts(cpPolyShape *poly, cpVect p, cpVect rot) { cpVect *src = poly->verts; cpVect *dst = poly->tVerts; cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY; cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY; for(int i=0; i<poly->numVerts; i++) { cpVect v = cpvadd(p, cpvrotate(src[i], rot)); dst[i] = v; l = cpfmin(l, v.x); r = cpfmax(r, v.x); b = cpfmin(b, v.y); t = cpfmax(t, v.y); } return cpBBNew(l, b, r, t); }
void cpArbiterApplyImpulse(cpArbiter *arb) { cpBody *a = arb->body_a; cpBody *b = arb->body_b; cpVect surface_vr = arb->surface_vr; cpFloat friction = arb->u; for(int i=0; i<arb->numContacts; i++){ cpContact *con = &arb->contacts[i]; cpFloat nMass = con->nMass; cpVect n = con->n; cpVect r1 = con->r1; cpVect r2 = con->r2; cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias)); cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias)); cpVect vr = relative_velocity(a, b, r1, r2); cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n); cpFloat vrn = cpvdot(vr, n); cpFloat vrt = cpvdot(cpvadd(vr, surface_vr), cpvperp(n)); cpFloat jbn = (con->bias - vbn)*nMass; cpFloat jbnOld = con->jBias; con->jBias = cpfmax(jbnOld + jbn, 0.0f); cpFloat jn = -(con->bounce + vrn)*nMass; cpFloat jnOld = con->jnAcc; con->jnAcc = cpfmax(jnOld + jn, 0.0f); cpFloat jtMax = friction*con->jnAcc; cpFloat jt = -vrt*con->tMass; cpFloat jtOld = con->jtAcc; con->jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax); apply_bias_impulses(a, b, r1, r2, cpvmult(n, con->jBias - jbnOld)); apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(con->jnAcc - jnOld, con->jtAcc - jtOld))); } }
static void DrawShape(cpShape *shape, DrawNode *renderer) { cpBody *body = cpShapeGetBody(shape); Color4F color = ColorForBody(body); switch (shape->CP_PRIVATE(klass)->type) { case CP_CIRCLE_SHAPE: { cpCircleShape *circle = (cpCircleShape *)shape; cpVect center = circle->tc; cpFloat radius = circle->r; renderer->drawDot(cpVert2Point(center), cpfmax(radius, 1.0), color); renderer->drawSegment(cpVert2Point(center), cpVert2Point(cpvadd(center, cpvmult(cpBodyGetRotation(body), radius))), 1.0, color); } break; case CP_SEGMENT_SHAPE: { cpSegmentShape *seg = (cpSegmentShape *)shape; renderer->drawSegment(cpVert2Point(seg->ta), cpVert2Point(seg->tb), cpfmax(seg->r, 2.0), color); } break; case CP_POLY_SHAPE: { cpPolyShape* poly = (cpPolyShape*)shape; Color4F line = color; line.a = cpflerp(color.a, 1.0, 0.5); int num = poly->count; Vec2* pPoints = new (std::nothrow) Vec2[num]; for(int i=0;i<num;++i) pPoints[i] = cpVert2Point(poly->planes[i].v0); renderer->drawPolygon(pPoints, num, color, 1.0, line); CC_SAFE_DELETE_ARRAY(pPoints); } break; default: cpAssertHard(false, "Bad assertion in DrawShape()"); } }
// Recursive implementatino of the GJK loop. static inline struct ClosestPoints GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const int iteration) { if(iteration > MAX_GJK_ITERATIONS) { cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); return ClosestPointsNew(v0, v1); } cpVect delta = cpvsub(v1.ab, v0.ab); // TODO: should this be an area2x check? if(cpvcross(delta, cpvadd(v0.ab, v1.ab)) > 0.0f) { // Origin is behind axis. Flip and try again. return GJKRecurse(ctx, v1, v0, iteration); } else { cpFloat t = ClosestT(v0.ab, v1.ab); cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(delta) : cpvneg(LerpT(v0.ab, v1.ab, t))); struct MinkowskiPoint p = Support(ctx, n); #if DRAW_GJK ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1)); cpVect c = cpvlerp(v0.ab, v1.ab, 0.5); ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1)); ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1)); #endif if( cpvcross(cpvsub(v1.ab, p.ab), cpvadd(v1.ab, p.ab)) > 0.0f && cpvcross(cpvsub(v0.ab, p.ab), cpvadd(v0.ab, p.ab)) < 0.0f ) { // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA. cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration); return EPA(ctx, v0, p, v1); } else { if(cpvdot(p.ab, n) <= cpfmax(cpvdot(v0.ab, n), cpvdot(v1.ab, n))) { // The edge v0, v1 that we already have is the closest to (0, 0) since p was not closer. cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); return ClosestPointsNew(v0, v1); } else { // p was closer to the origin than our existing edge. // Need to figure out which existing point to drop. if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)) { return GJKRecurse(ctx, v0, p, iteration + 1); } else { return GJKRecurse(ctx, p, v1, iteration + 1); } } } } }
static void DrawShape(cpShape *shape, DrawNode *renderer) { cpBody *body = shape->body; Color4F color = ColorForBody(body); switch (shape->CP_PRIVATE(klass)->type) { case CP_CIRCLE_SHAPE: { cpCircleShape *circle = (cpCircleShape *)shape; cpVect center = circle->tc; cpFloat radius = circle->r; renderer->drawDot(cpVert2Point(center), cpfmax(radius, 1.0), color); renderer->drawSegment(cpVert2Point(center), cpVert2Point(cpvadd(center, cpvmult(body->rot, radius))), 1.0, color); } break; case CP_SEGMENT_SHAPE: { cpSegmentShape *seg = (cpSegmentShape *)shape; renderer->drawSegment(cpVert2Point(seg->ta), cpVert2Point(seg->tb), cpfmax(seg->r, 2.0), color); } break; case CP_POLY_SHAPE: { cpPolyShape *poly = (cpPolyShape *)shape; Color4F line = color; line.a = cpflerp(color.a, 1.0, 0.5); Vec2* pPoints = cpVertArray2ccpArrayN(poly->tVerts, poly->numVerts); renderer->drawPolygon(pPoints, poly->numVerts, color, 1.0, line); CC_SAFE_DELETE_ARRAY(pPoints); } break; default: cpAssertHard(false, "Bad assertion in DrawShape()"); } }
static inline struct ClosestPoints GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const int iteration) { if(iteration > MAX_GJK_ITERATIONS){ cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); return ClosestPointsNew(v0, v1); } cpVect delta = cpvsub(v1.ab, v0.ab); if(cpvcross(delta, cpvadd(v0.ab, v1.ab)) > 0.0f){ // Origin is behind axis. Flip and try again. return GJKRecurse(ctx, v1, v0, iteration + 1); } else { cpFloat t = ClosestT(v0.ab, v1.ab); cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(delta) : cpvneg(LerpT(v0.ab, v1.ab, t))); struct MinkowskiPoint p = Support(ctx, n); #if DRAW_GJK ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1)); cpVect c = cpvlerp(v0.ab, v1.ab, 0.5); ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1)); ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1)); #endif if( cpvcross(cpvsub(v1.ab, p.ab), cpvadd(v1.ab, p.ab)) > 0.0f && cpvcross(cpvsub(v0.ab, p.ab), cpvadd(v0.ab, p.ab)) < 0.0f ){ cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration); // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA. return EPA(ctx, v0, p, v1); } else { // The new point must be farther along the normal than the existing points. if(cpvdot(p.ab, n) <= cpfmax(cpvdot(v0.ab, n), cpvdot(v1.ab, n))){ cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); return ClosestPointsNew(v0, v1); } else { if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)){ return GJKRecurse(ctx, v0, p, iteration + 1); } else { return GJKRecurse(ctx, p, v1, iteration + 1); } } } } }
static void update(cpSpace *space, double dt) { // Set the first anchor point (the one attached to the static body) of the dolly servo to the mouse's x position. cpPivotJointSetAnchorA(dollyServo, cpv(ChipmunkDemoMouse.x, 100)); // Set the max length of the winch servo to match the mouse's height. cpSlideJointSetMax(winchServo, cpfmax(100 - ChipmunkDemoMouse.y, 50)); if(hookJoint && ChipmunkDemoRightClick){ cpSpaceRemoveConstraint(space, hookJoint); cpConstraintFree(hookJoint); hookJoint = NULL; } cpSpaceStep(space, dt); }
static inline cpBB GetBB(cpBBTree *tree, void *obj) { cpBB bb = tree->spatialIndex.bbfunc(obj); cpBBTreeVelocityFunc velocityFunc = tree->velocityFunc; if(velocityFunc){ cpFloat coef = 0.1f; cpFloat x = (bb.r - bb.l)*coef; cpFloat y = (bb.t - bb.b)*coef; cpVect v = cpvmult(velocityFunc(obj), 0.1f); return cpBBNew(bb.l + cpfmin(-x, v.x), bb.b + cpfmin(-y, v.y), bb.r + cpfmax(x, v.x), bb.t + cpfmax(y, v.y)); } else { return bb; } }
static void update(int ticks) { if(arrowDirection.y){ circleRadius = cpfmax(10.0f, circleRadius + arrowDirection.y); for(int i=0; i<NUM_CIRCLES; i++){ circles[i]->body->m = cpMomentForCircle(1.0f, 0.0f, circleRadius, cpvzero); cpCircleShapeSetRadius(circles[i], circleRadius); } } int steps = 1; cpFloat dt = 1.0f/60.0f/(cpFloat)steps; for(int i=0; i<steps; i++){ cpSpaceStep(space, dt); } }
static void update(int ticks) { int steps = 1; cpFloat dt = 1.0f/60.0f/(cpFloat)steps; for(int i=0; i<steps; i++){ // Set the first anchor point (the one attached to the static body) of the dolly servo to the mouse's x position. cpPivotJointSetAnchr1(dollyServo, cpv(ChipmunkDemoMouse.x, 100)); // Set the max length of the winch servo to match the mouse's height. cpSlideJointSetMax(winchServo, cpfmax(100 - ChipmunkDemoMouse.y, 50)); if(hookJoint && ChipmunkDemoKeyboard.y < 0){ cpSpaceRemoveConstraint(space, hookJoint); cpConstraintFree(hookJoint); hookJoint = NULL; } cpSpaceStep(space, dt); } }
cpShape * cpSpaceNearestPointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpLayers layers, cpGroup group, cpNearestPointQueryInfo *out) { cpNearestPointQueryInfo info = {NULL, cpvzero, maxDistance, cpvzero}; if(out){ (*out) = info; } else { out = &info; } struct NearestPointQueryContext context = { point, maxDistance, layers, group, NULL }; cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); cpSpatialIndexQuery(space->activeShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out); cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out); return out->shape; }
cpBool cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n){ return cpvdot(p, n) <= cpfmax(cpvdot(v0, n), cpvdot(v1, n)); }