Merge pull request #2876 from ricardoquesada/chipmunk_6_1_5

updates chipmunk2d to v6.1.5
This commit is contained in:
minggo 2013-06-12 22:53:35 -07:00
commit 6ef681bfff
17 changed files with 233 additions and 98 deletions

View File

@ -22,6 +22,10 @@
#ifndef CHIPMUNK_HEADER
#define CHIPMUNK_HEADER
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#endif
#include <stdlib.h>
#include <math.h>
@ -34,26 +38,26 @@ extern "C" {
#endif
#if CP_ALLOW_PRIVATE_ACCESS == 1
#define CP_PRIVATE(symbol) symbol
#define CP_PRIVATE(__symbol__) __symbol__
#else
#define CP_PRIVATE(symbol) symbol##_private
#define CP_PRIVATE(__symbol__) __symbol__##_private
#endif
void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...);
#ifdef NDEBUG
#define cpAssertWarn(condition, ...)
#define cpAssertWarn(__condition__, ...)
#else
#define cpAssertWarn(condition, ...) if(!(condition)) cpMessage(#condition, __FILE__, __LINE__, 0, 0, __VA_ARGS__)
#define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__)
#endif
#ifdef NDEBUG
#define cpAssertSoft(condition, ...)
#define cpAssertSoft(__condition__, ...)
#else
#define cpAssertSoft(condition, ...) if(!(condition)) cpMessage(#condition, __FILE__, __LINE__, 1, 0, __VA_ARGS__)
#define cpAssertSoft(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__)
#endif
// Hard assertions are important and cheap to execute. They are not disabled by compiling as debug.
#define cpAssertHard(condition, ...) if(!(condition)) cpMessage(#condition, __FILE__, __LINE__, 1, 1, __VA_ARGS__)
#define cpAssertHard(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__)
#include "chipmunk_types.h"
@ -106,10 +110,10 @@ typedef struct cpSpace cpSpace;
#include "cpSpace.h"
// Chipmunk 6.1.2
// Chipmunk 6.1.5
#define CP_VERSION_MAJOR 6
#define CP_VERSION_MINOR 1
#define CP_VERSION_RELEASE 2
#define CP_VERSION_RELEASE 5
/// Version string.
extern const char *cpVersionString;

View File

@ -27,9 +27,12 @@ MAKE_REF(cpvcross);
MAKE_REF(cpvperp);
MAKE_REF(cpvrperp);
MAKE_REF(cpvproject);
MAKE_REF(cpvforangle);
MAKE_REF(cpvtoangle);
MAKE_REF(cpvrotate);
MAKE_REF(cpvunrotate);
MAKE_REF(cpvlengthsq);
MAKE_REF(cpvlength);
MAKE_REF(cpvlerp);
MAKE_REF(cpvnormalize);
MAKE_REF(cpvnormalize_safe);
@ -57,6 +60,7 @@ MAKE_REF(cpBBArea);
MAKE_REF(cpBBMergedArea);
MAKE_REF(cpBBSegmentQuery);
MAKE_REF(cpBBIntersectsSegment);
MAKE_REF(cpBBClampVect);
MAKE_REF(cpBodyGetMass);
MAKE_REF(cpBodyGetMoment);

View File

@ -4,7 +4,7 @@
#include "TargetConditionals.h"
#endif
#if (TARGET_OS_IPHONE == 1) || (TARGET_OS_MAC == 1) && (!defined CP_USE_CGPOINTS)
#if ((TARGET_OS_IPHONE == 1) || (TARGET_OS_MAC == 1)) && (!defined CP_USE_CGPOINTS)
#define CP_USE_CGPOINTS 1
#endif

View File

@ -38,6 +38,7 @@ typedef struct cpDampedRotarySpring {
cpFloat w_coef;
cpFloat iSum;
cpFloat jAcc;
} cpDampedRotarySpring;
/// Allocate a damped rotary spring.

View File

@ -43,6 +43,8 @@ struct cpDampedSpring {
cpVect r1, r2;
cpFloat nMass;
cpVect n;
cpFloat jAcc;
};
/// Allocate a damped spring.

View File

@ -117,7 +117,15 @@ CP_DefineArbiterStructSetter(type, member, name)
CP_DefineArbiterStructProperty(cpFloat, e, Elasticity)
CP_DefineArbiterStructProperty(cpFloat, u, Friction)
CP_DefineArbiterStructProperty(cpVect, surface_vr, SurfaceVelocity)
// Get the relative surface velocity of the two shapes in contact.
cpVect cpArbiterGetSurfaceVelocity(cpArbiter *arb);
// Override the relative surface velocity of the two shapes in contact.
// By default this is calculated to be the difference of the two
// surface velocities clamped to the tangent plane.
void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr);
CP_DefineArbiterStructProperty(cpDataPointer, data, UserData)
/// Calculate the total impulse that was applied by this arbiter.
@ -148,7 +156,7 @@ static inline void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape
}
}
/// A macro shortcut for defining and retrieving the shapes from an arbiter.
#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
#define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__);
/// Return the colliding bodies involved for this arbiter.
/// The order of the cpSpace.collision_type the bodies are associated with values will match
@ -160,7 +168,7 @@ static inline void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody *
(*b) = shape_b->body;
}
/// A macro shortcut for defining and retrieving the bodies from an arbiter.
#define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);
#define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__);
/// A struct that wraps up the important collision data for an arbiter.
typedef struct cpContactPointSet {
@ -177,9 +185,14 @@ typedef struct cpContactPointSet {
cpFloat dist;
} points[CP_MAX_CONTACTS_PER_ARBITER];
} cpContactPointSet;
/// Return a contact set from an arbiter.
cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb);
/// Replace the contact point set for an arbiter.
/// This can be a very powerful feature, but use it with caution!
void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set);
/// Returns true if this is the first step a pair of objects started colliding.
cpBool cpArbiterIsFirstContact(const cpArbiter *arb);
/// Get the number of contact points for this arbiter.

View File

@ -148,6 +148,7 @@ static inline cpBool cpBodyIsStatic(const cpBody *body)
}
/// Returns true if the body has not been added to a space.
/// Note: Static bodies are a subtype of rogue bodies.
static inline cpBool cpBodyIsRogue(const cpBody *body)
{
return (body->CP_PRIVATE(space) == ((cpSpace*)0));

View File

@ -201,6 +201,13 @@ cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body);
/// Test if a constraint has been added to the space.
cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint);
/// Convert a dynamic rogue body to a static one.
/// If the body is active, you must remove it from the space first.
void cpSpaceConvertBodyToStatic(cpSpace *space, cpBody *body);
/// Convert a body to a dynamic rogue body.
/// If you want the body to be active after the transition, you must add it to the space also.
void cpSpaceConvertBodyToDynamic(cpSpace *space, cpBody *body, cpFloat mass, cpFloat moment);
/// Post Step callback function type.
typedef void (*cpPostStepFunc)(cpSpace *space, void *key, void *data);
/// Schedule a post-step callback to be called when cpSpaceStep() finishes.

View File

@ -8,8 +8,13 @@ if(BUILD_SHARED)
add_library(chipmunk SHARED
${chipmunk_source_files}
)
# Tell MSVC to compile the code as C++.
if(MSVC)
set_source_files_properties(${chipmunk_source_files} PROPERTIES LANGUAGE CXX)
set_target_properties(chipmunk PROPERTIES LINKER_LANGUAGE CXX)
endif(MSVC)
# set the lib's version number
set_target_properties(chipmunk PROPERTIES VERSION 6.1.2)
set_target_properties(chipmunk PROPERTIES VERSION 6.1.5)
install(TARGETS chipmunk RUNTIME DESTINATION lib LIBRARY DESTINATION lib)
endif(BUILD_SHARED)
@ -17,6 +22,11 @@ if(BUILD_STATIC)
add_library(chipmunk_static STATIC
${chipmunk_source_files}
)
# Tell MSVC to compile the code as C++.
if(MSVC)
set_source_files_properties(${chipmunk_source_files} PROPERTIES LANGUAGE CXX)
set_target_properties(chipmunk_static PROPERTIES LINKER_LANGUAGE CXX)
endif(MSVC)
# Sets chipmunk_static to output "libchipmunk.a" not "libchipmunk_static.a"
set_target_properties(chipmunk_static PROPERTIES OUTPUT_NAME chipmunk)
if(INSTALL_STATIC)

View File

@ -42,6 +42,8 @@ preStep(cpDampedRotarySpring *spring, cpFloat dt)
// apply spring torque
cpFloat j_spring = spring->springTorqueFunc((cpConstraint *)spring, a->a - b->a)*dt;
spring->jAcc = j_spring;
a->w -= j_spring*a->i_inv;
b->w += j_spring*b->i_inv;
}
@ -64,14 +66,16 @@ applyImpulse(cpDampedRotarySpring *spring, cpFloat dt)
//apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, v_damp*spring->nMass));
cpFloat j_damp = w_damp*spring->iSum;
spring->jAcc += j_damp;
a->w += j_damp*a->i_inv;
b->w -= j_damp*b->i_inv;
}
static cpFloat
getImpulse(cpConstraint *constraint)
getImpulse(cpDampedRotarySpring *spring)
{
return 0.0f;
return spring->jAcc;
}
static const cpConstraintClass klass = {
@ -98,6 +102,8 @@ cpDampedRotarySpringInit(cpDampedRotarySpring *spring, cpBody *a, cpBody *b, cpF
spring->damping = damping;
spring->springTorqueFunc = (cpDampedRotarySpringTorqueFunc)defaultSpringTorque;
spring->jAcc = 0.0f;
return spring;
}

View File

@ -49,7 +49,8 @@ preStep(cpDampedSpring *spring, cpFloat dt)
// apply spring force
cpFloat f_spring = spring->springForceFunc((cpConstraint *)spring, dist);
apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, f_spring*dt));
cpFloat j_spring = spring->jAcc = f_spring*dt;
apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_spring));
}
static void applyCachedImpulse(cpDampedSpring *spring, cpFloat dt_coef){}
@ -71,13 +72,15 @@ applyImpulse(cpDampedSpring *spring, cpFloat dt)
cpFloat v_damp = (spring->target_vrn - vrn)*spring->v_coef;
spring->target_vrn = vrn + v_damp;
apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, v_damp*spring->nMass));
cpFloat j_damp = v_damp*spring->nMass;
spring->jAcc += j_damp;
apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_damp));
}
static cpFloat
getImpulse(cpConstraint *constraint)
getImpulse(cpDampedSpring *spring)
{
return 0.0f;
return spring->jAcc;
}
static const cpConstraintClass klass = {
@ -107,6 +110,8 @@ cpDampedSpringInit(cpDampedSpring *spring, cpBody *a, cpBody *b, cpVect anchr1,
spring->damping = damping;
spring->springForceFunc = (cpDampedSpringForceFunc)defaultSpringForce;
spring->jAcc = 0.0f;
return spring;
}

View File

@ -48,7 +48,10 @@ unthreadHelper(cpArbiter *arb, cpBody *body)
if(prev){
cpArbiterThreadForBody(prev, body)->next = next;
} else {
} else if(body->arbiterList == arb) {
// IFF prev is NULL and body->arbiterList == arb, is arb at the head of the list.
// This function may be called for an arbiter that was never in a list.
// In that case, we need to protect it from wiping out the body->arbiterList pointer.
body->arbiterList = next;
}
@ -107,8 +110,7 @@ cpArbiterGetContactPointSet(const cpArbiter *arb)
cpContactPointSet set;
set.count = cpArbiterGetCount(arb);
int i;
for(i=0; i<set.count; i++){
for(int i=0; i<set.count; i++){
set.points[i].point = arb->CP_PRIVATE(contacts)[i].CP_PRIVATE(p);
set.points[i].normal = arb->CP_PRIVATE(contacts)[i].CP_PRIVATE(n);
set.points[i].dist = arb->CP_PRIVATE(contacts)[i].CP_PRIVATE(dist);
@ -117,6 +119,18 @@ cpArbiterGetContactPointSet(const cpArbiter *arb)
return set;
}
void
cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set)
{
int count = set->count;
cpAssertHard(count == arb->numContacts, "The number of contact points cannot be changed.");
for(int i=0; i<count; i++){
arb->contacts[i].p = set->points[i].point;
arb->contacts[i].n = set->points[i].normal;
arb->contacts[i].dist = set->points[i].dist;
}
}
cpVect
cpArbiterTotalImpulse(const cpArbiter *arb)
@ -164,30 +178,25 @@ cpArbiterTotalKE(const cpArbiter *arb)
return sum;
}
//cpFloat
//cpContactsEstimateCrushingImpulse(cpContact *contacts, int numContacts)
//{
// cpFloat fsum = 0.0f;
// cpVect vsum = cpvzero;
//
// for(int i=0; i<numContacts; i++){
// cpContact *con = &contacts[i];
// cpVect j = cpvrotate(con->n, cpv(con->jnAcc, con->jtAcc));
//
// fsum += cpvlength(j);
// vsum = cpvadd(vsum, j);
// }
//
// cpFloat vmag = cpvlength(vsum);
// return (1.0f - vmag/fsum);
//}
void
cpArbiterIgnore(cpArbiter *arb)
{
arb->state = cpArbiterStateIgnore;
}
cpVect
cpArbiterGetSurfaceVelocity(cpArbiter *arb)
{
return cpvmult(arb->surface_vr, arb->swappedColl ? -1.0f : 1.0);
}
void
cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr)
{
arb->surface_vr = cpvmult(vr, arb->swappedColl ? -1.0f : 1.0);
}
cpArbiter*
cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b)
{
@ -220,21 +229,18 @@ cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b)
void
cpArbiterUpdate(cpArbiter *arb, cpContact *contacts, int numContacts, cpCollisionHandler *handler, cpShape *a, cpShape *b)
{
// Arbiters without contact data may exist if a collision function rejected the collision.
if(arb->contacts){
// Iterate over the possible pairs to look for hash value matches.
for(int i=0; i<arb->numContacts; i++){
cpContact *old = &arb->contacts[i];
// Iterate over the possible pairs to look for hash value matches.
for(int i=0; i<numContacts; i++){
cpContact *con = &contacts[i];
for(int j=0; j<arb->numContacts; j++){
cpContact *old = &arb->contacts[j];
for(int j=0; j<numContacts; j++){
cpContact *new_contact = &contacts[j];
// This could trigger false positives, but is fairly unlikely nor serious if it does.
if(new_contact->hash == old->hash){
// Copy the persistant contact information.
new_contact->jnAcc = old->jnAcc;
new_contact->jtAcc = old->jtAcc;
}
// This could trigger false positives, but is fairly unlikely nor serious if it does.
if(con->hash == old->hash){
// Copy the persistant contact information.
con->jnAcc = old->jnAcc;
con->jtAcc = old->jtAcc;
}
}
}
@ -247,7 +253,12 @@ cpArbiterUpdate(cpArbiter *arb, cpContact *contacts, int numContacts, cpCollisio
arb->e = a->e * b->e;
arb->u = a->u * b->u;
arb->surface_vr = cpvsub(a->surface_v, b->surface_v);
// Currently all contacts will have the same normal.
// This may change in the future.
cpVect n = (numContacts ? contacts[0].n : cpvzero);
cpVect surface_vr = cpvsub(a->surface_v, b->surface_v);
arb->surface_vr = cpvsub(surface_vr, cpvmult(n, cpvdot(surface_vr, n)));
// For collisions between two similar primitive types, the order could have been swapped.
arb->a = a; arb->body_a = a->body;
@ -317,11 +328,11 @@ cpArbiterApplyImpulse(cpArbiter *arb)
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);
cpVect vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr);
cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n);
cpFloat vrn = cpvdot(vr, n);
cpFloat vrt = cpvdot(cpvadd(vr, surface_vr), cpvperp(n));
cpFloat vrt = cpvdot(vr, cpvperp(n));
cpFloat jbn = (con->bias - vbn)*nMass;
cpFloat jbnOld = con->jBias;

View File

@ -133,7 +133,16 @@ cpShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info
info = &blank;
}
shape->klass->segmentQuery(shape, a, b, info);
cpNearestPointQueryInfo nearest;
shape->klass->nearestPointQuery(shape, a, &nearest);
if(nearest.d <= 0.0){
info->shape = shape;
info->t = 0.0;
info->n = cpvnormalize(cpvsub(a, nearest.p));
} else {
shape->klass->segmentQuery(shape, a, b, info);
}
return (info->shape != NULL);
}
@ -165,13 +174,12 @@ cpCicleShapeNearestPointQuery(cpCircleShape *circle, cpVect p, cpNearestPointQue
static void
circleSegmentQuery(cpShape *shape, cpVect center, cpFloat r, cpVect a, cpVect b, cpSegmentQueryInfo *info)
{
// offset the line to be relative to the circle
a = cpvsub(a, center);
b = cpvsub(b, center);
cpVect da = cpvsub(a, center);
cpVect db = cpvsub(b, center);
cpFloat qa = cpvdot(a, a) - 2.0f*cpvdot(a, b) + cpvdot(b, b);
cpFloat qb = -2.0f*cpvdot(a, a) + 2.0f*cpvdot(a, b);
cpFloat qc = cpvdot(a, a) - r*r;
cpFloat qa = cpvdot(da, da) - 2.0f*cpvdot(da, db) + cpvdot(db, db);
cpFloat qb = -2.0f*cpvdot(da, da) + 2.0f*cpvdot(da, db);
cpFloat qc = cpvdot(da, da) - r*r;
cpFloat det = qb*qb - 4.0f*qa*qc;
@ -180,7 +188,7 @@ circleSegmentQuery(cpShape *shape, cpVect center, cpFloat r, cpVect a, cpVect b,
if(0.0f<= t && t <= 1.0f){
info->shape = shape;
info->t = t;
info->n = cpvnormalize(cpvlerp(a, b, t));
info->n = cpvnormalize(cpvlerp(da, db, t));
}
}
}

View File

@ -185,7 +185,7 @@ cpSpaceFree(cpSpace *space)
#define cpAssertSpaceUnlocked(space) \
cpAssertHard(!space->locked, \
"This addition/removal cannot be done safely during a call to cpSpaceStep() or during a query. " \
"This operation cannot be done safely during a call to cpSpaceStep() or during a query. " \
"Put these calls into a post-step callback." \
);
@ -259,7 +259,8 @@ cpSpaceAddShape(cpSpace *space, cpShape *shape)
cpBody *body = shape->body;
if(cpBodyIsStatic(body)) return cpSpaceAddStaticShape(space, shape);
cpAssertHard(!shape->space, "This shape is already added to a space and cannot be added to another.");
cpAssertHard(shape->space != space, "You have already added this shape to this space. You must not add it a second time.");
cpAssertHard(!shape->space, "You have already added this shape to another space. You cannot add it to a second.");
cpAssertSpaceUnlocked(space);
cpBodyActivate(body);
@ -275,7 +276,9 @@ cpSpaceAddShape(cpSpace *space, cpShape *shape)
cpShape *
cpSpaceAddStaticShape(cpSpace *space, cpShape *shape)
{
cpAssertHard(!shape->space, "This shape is already added to a space and cannot be added to another.");
cpAssertHard(shape->space != space, "You have already added this shape to this space. You must not add it a second time.");
cpAssertHard(!shape->space, "You have already added this shape to another space. You cannot add it to a second.");
cpAssertHard(cpBodyIsRogue(shape->body), "You are adding a static shape to a dynamic body. Did you mean to attach it to a static or rogue body? See the documentation for more information.");
cpAssertSpaceUnlocked(space);
cpBody *body = shape->body;
@ -290,8 +293,9 @@ cpSpaceAddStaticShape(cpSpace *space, cpShape *shape)
cpBody *
cpSpaceAddBody(cpSpace *space, cpBody *body)
{
cpAssertHard(!cpBodyIsStatic(body), "Static bodies cannot be added to a space as they are not meant to be simulated.");
cpAssertHard(!body->space, "This body is already added to a space and cannot be added to another.");
cpAssertHard(!cpBodyIsStatic(body), "Do not add static bodies to a space. Static bodies do not move and should not be simulated.");
cpAssertHard(body->space != space, "You have already added this body to this space. You must not add it a second time.");
cpAssertHard(!body->space, "You have already added this body to another space. You cannot add it to a second.");
cpAssertSpaceUnlocked(space);
cpArrayPush(space->bodies, body);
@ -303,7 +307,9 @@ cpSpaceAddBody(cpSpace *space, cpBody *body)
cpConstraint *
cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint)
{
cpAssertHard(!constraint->space, "This shape is already added to a space and cannot be added to another.");
cpAssertHard(constraint->space != space, "You have already added this constraint to this space. You must not add it a second time.");
cpAssertHard(!constraint->space, "You have already added this constraint to another space. You cannot add it to a second.");
cpAssertHard(constraint->a && constraint->b, "Constraint is attached to a NULL body.");
cpAssertSpaceUnlocked(space);
cpBodyActivate(constraint->a);
@ -433,6 +439,45 @@ cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint)
return (constraint->space == space);
}
//MARK: Static/rogue body conversion.
void
cpSpaceConvertBodyToStatic(cpSpace *space, cpBody *body)
{
cpAssertHard(!cpBodyIsStatic(body), "Body is already static.");
cpAssertHard(cpBodyIsRogue(body), "Remove the body from the space before calling this function.");
cpAssertSpaceUnlocked(space);
cpBodySetMass(body, INFINITY);
cpBodySetMoment(body, INFINITY);
cpBodySetVel(body, cpvzero);
cpBodySetAngVel(body, 0.0f);
body->node.idleTime = INFINITY;
CP_BODY_FOREACH_SHAPE(body, shape){
cpSpatialIndexRemove(space->activeShapes, shape, shape->hashid);
cpSpatialIndexInsert(space->staticShapes, shape, shape->hashid);
}
}
void
cpSpaceConvertBodyToDynamic(cpSpace *space, cpBody *body, cpFloat m, cpFloat i)
{
cpAssertHard(cpBodyIsStatic(body), "Body is already dynamic.");
cpAssertSpaceUnlocked(space);
cpBodyActivateStatic(body, NULL);
cpBodySetMass(body, m);
cpBodySetMoment(body, i);
body->node.idleTime = 0.0f;
CP_BODY_FOREACH_SHAPE(body, shape){
cpSpatialIndexRemove(space->staticShapes, shape, shape->hashid);
cpSpatialIndexInsert(space->activeShapes, shape, shape->hashid);
}
}
//MARK: Iteration

View File

@ -28,12 +28,13 @@
void
cpSpaceActivateBody(cpSpace *space, cpBody *body)
{
cpAssertHard(!cpBodyIsRogue(body), "Internal error: Attempting to activate a rouge body.");
cpAssertHard(!cpBodyIsRogue(body), "Internal error: Attempting to activate a rogue body.");
if(space->locked){
// cpSpaceActivateBody() is called again once the space is unlocked
if(!cpArrayContains(space->rousedBodies, body)) cpArrayPush(space->rousedBodies, body);
} else {
cpAssertSoft(body->node.root == NULL && body->node.next == NULL, "Internal error: Activating body non-NULL node pointers.");
cpArrayPush(space->bodies, body);
CP_BODY_FOREACH_SHAPE(body, shape){
@ -43,6 +44,11 @@ cpSpaceActivateBody(cpSpace *space, cpBody *body)
CP_BODY_FOREACH_ARBITER(body, arb){
cpBody *bodyA = arb->body_a;
// Arbiters are shared between two bodies that are always woken up together.
// You only want to restore the arbiter once, so bodyA is arbitrarily chosen to own the arbiter.
// The edge case is when static bodies are involved as the static bodies never actually sleep.
// If the static body is bodyB then all is good. If the static body is bodyA, that can easily be checked.
if(body == bodyA || cpBodyIsStatic(bodyA)){
int numContacts = arb->numContacts;
cpContact *contacts = arb->contacts;
@ -140,6 +146,13 @@ cpBodyActivate(cpBody *body)
body->node.idleTime = 0.0f;
ComponentActivate(ComponentRoot(body));
}
CP_BODY_FOREACH_ARBITER(body, arb){
// Reset the idle timer of things the body is touching as well.
// That way things don't get left hanging in the air.
cpBody *other = (arb->body_a == body ? arb->body_b : arb->body_a);
if(!cpBodyIsStatic(other)) other->node.idleTime = 0.0f;
}
}
void
@ -300,10 +313,9 @@ cpBodySleep(cpBody *body)
void
cpBodySleepWithGroup(cpBody *body, cpBody *group){
cpAssertHard(!cpBodyIsStatic(body) && !cpBodyIsRogue(body), "Rogue and static bodies cannot be put to sleep.");
cpAssertHard(!cpBodyIsRogue(body), "Rogue (and static) bodies cannot be put to sleep.");
cpSpace *space = body->space;
cpAssertHard(space, "Cannot put a rogue body to sleep.");
cpAssertHard(!space->locked, "Bodies cannot be put to sleep during a query or a call to cpSpaceStep(). Put these calls into a post-step callback.");
cpAssertHard(group == NULL || cpBodyIsSleeping(group), "Cannot use a non-sleeping body as a group identifier.");

View File

@ -29,7 +29,7 @@ cpSpaceGetPostStepCallback(cpSpace *space, void *key)
cpArray *arr = space->postStepCallbacks;
for(int i=0; i<arr->num; i++){
cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];
if(callback->key == key) return callback;
if(callback && callback->key == key) return callback;
}
return NULL;
@ -71,31 +71,36 @@ cpSpaceUnlock(cpSpace *space, cpBool runPostStep)
space->locked--;
cpAssertHard(space->locked >= 0, "Internal Error: Space lock underflow.");
if(space->locked == 0 && runPostStep && !space->skipPostStep){
space->skipPostStep = cpTrue;
if(space->locked == 0){
cpArray *waking = space->rousedBodies;
for(int i=0, count=waking->num; i<count; i++){
cpSpaceActivateBody(space, (cpBody *)waking->arr[i]);
waking->arr[i] = NULL;
}
cpArray *arr = space->postStepCallbacks;
for(int i=0; i<arr->num; i++){
cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];
cpPostStepFunc func = callback->func;
// Mark the func as NULL in case calling it calls cpSpaceRunPostStepCallbacks() again.
callback->func = NULL;
if(func) func(space, callback->key, callback->data);
arr->arr[i] = NULL;
cpfree(callback);
}
waking->num = 0;
arr->num = 0;
space->skipPostStep = cpFalse;
if(space->locked == 0 && runPostStep && !space->skipPostStep){
space->skipPostStep = cpTrue;
cpArray *arr = space->postStepCallbacks;
for(int i=0; i<arr->num; i++){
cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];
cpPostStepFunc func = callback->func;
// Mark the func as NULL in case calling it calls cpSpaceRunPostStepCallbacks() again.
// TODO need more tests around this case I think.
callback->func = NULL;
if(func) func(space, callback->key, callback->data);
arr->arr[i] = NULL;
cpfree(callback);
}
arr->num = 0;
space->skipPostStep = cpFalse;
}
}
}

View File

@ -23,14 +23,15 @@
#include "chipmunk_private.h"
inline cpVect
cpVect
cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t)
{
cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));
cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));
if(omega == 0.0){
return v1;
if(omega < 1e-3){
// If the angle between two vectors is very small, lerp instead to avoid precision issues.
return cpvlerp(v1, v2, t);
} else {
cpFloat denom = 1.0f/cpfsin(omega);
return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom));