Mercurial > projects > chipmunkd
changeset 4:7ebbd4d05553
initial commit
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/chipmunk.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,174 @@ + +// written in the D programming language + +module chipmunkd.chipmunk; +//#ifndef CHIPMUNK_HEADER +//#define CHIPMUNK_HEADER +// +//#ifdef __cplusplus +//extern "C" { +//#endif +// +//void cpMessage(const char *message, const char *condition, const char *file, int line, int isError); +//#ifdef NDEBUG +// #define cpAssertWarn(condition, message) +//#else +// #define cpAssertWarn(condition, message) if(!(condition)) cpMessage(message, #condition, __FILE__, __LINE__, 0) +//#endif + +debug +{ + //TODO: + //void cpAssertWarn(bool condition, string message){if(condition) cpMessage(message,condition,__FILE__,__LINE__, 0);} + void cpAssertWarn(bool condition, string message){} +} +else +{ + void cpAssertWarn(bool condition, string message){} +} + +// +//#ifdef NDEBUG +// #define cpAssert(condition, message) +//#else +// #define cpAssert(condition, message) if(!(condition)) cpMessage(message, #condition, __FILE__, __LINE__, 1) +//#endif +// +import chipmunkd.chipmunk_types_h; +import core.stdc.stdlib; +// +//#ifndef INFINITY +// #ifdef _MSC_VER +// union MSVC_EVIL_FLOAT_HACK +// { +// unsigned __int8 Bytes[4]; +// float Value; +// }; +// static union MSVC_EVIL_FLOAT_HACK INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}}; +// #define INFINITY (INFINITY_HACK.Value) +// #endif +// +// #ifdef __GNUC__ +// #define INFINITY (__builtin_inf()) +// #endif +// +// #ifndef INFINITY +// #define INFINITY (1e1000) +// #endif +//#endif + +enum INFINITY = cpFloat.infinity; + +// Maximum allocated size for various Chipmunk buffer sizes +enum CP_BUFFER_BYTES = (32*1024); + +alias core.stdc.stdlib.malloc cpmalloc; +alias core.stdc.stdlib.calloc cpcalloc; +alias core.stdc.stdlib.realloc cprealloc; +alias core.stdc.stdlib.free cpfree; + +public import chipmunkd.cpVect_h,chipmunkd.cpVect; +public import chipmunkd.cpBB; +public import chipmunkd.cpArray; +public import chipmunkd.cpHashSet; +public import chipmunkd.cpSpaceHash; +// +public import chipmunkd.cpBody; +public import chipmunkd.cpShape; +public import chipmunkd.cpPolyShape; +// +public import chipmunkd.cpArbiter; +public import chipmunkd.cpCollision; +// +public import chipmunkd.constraints.cpConstraint; +// +public import chipmunkd.cpSpace; +public import chipmunkd.cpSpaceComponent; +public import chipmunkd.cpSpaceQuery; +public import chipmunkd.cpSpaceStep; + +public import chipmunkd.chipmunk_types_h; + +enum cpHashValue CP_HASH_COEF = cast(cpHashValue)(3344921057uL); // ulong to uint ?? +static cpHashValue CP_HASH_PAIR(T)(T A, T B) {return (cast(cpHashValue)(A)*CP_HASH_COEF ^ cast(cpHashValue)(B)*CP_HASH_COEF);} + +void +cpMessage(string message, string condition, string file, int line, int isError) +{ + //TODO: + //fprintf(stderr, (isError ? "Aborting due to Chipmunk error: %s\n" : "Chipmunk warning: %s\n"), message); + //fprintf(stderr, "\tFailed condition: %s\n", condition); + //fprintf(stderr, "\tSource:%s:%d\n", file, line); + + if(isError) abort(); +} + +extern const char *cpVersionString; +void cpInitChipmunk() +{ + cpInitCollisionFuncs(); +} + +/** + Calculate the moment of inertia for a circle. + r1 and r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. +*/ +cpFloat +cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset) +{ + return (1.0f/2.0f)*m*(r1*r1 + r2*r2) + m*cpvdot(offset, offset); +} +/** + Calculate the moment of inertia for a line segment. + Beveling radius is not supported. +*/ +cpFloat +cpMomentForSegment(cpFloat m, cpVect a, cpVect b) +{ + cpFloat length = cpvlength(cpvsub(b, a)); + cpVect offset = cpvmult(cpvadd(a, b), 1.0f/2.0f); + + return m*length*length/12.0f + m*cpvdot(offset, offset); +} + +/** + Calculate the moment of inertia for a solid polygon shape. +*/ +cpFloat +cpMomentForPoly(cpFloat m, const int numVerts, cpVect *verts, cpVect offset) +{ + cpVect *tVerts = cast(cpVect *)cpcalloc(numVerts, cpVect.sizeof); + for(int i=0; i<numVerts; i++) + tVerts[i] = cpvadd(verts[i], offset); + + cpFloat sum1 = 0.0f; + cpFloat sum2 = 0.0f; + for(int i=0; i<numVerts; i++){ + cpVect v1 = tVerts[i]; + cpVect v2 = tVerts[(i+1)%numVerts]; + + cpFloat a = cpvcross(v2, v1); + cpFloat b = cpvdot(v1, v1) + cpvdot(v1, v2) + cpvdot(v2, v2); + + sum1 += a*b; + sum2 += a; + } + + cpfree(tVerts); + return (m*sum1)/(6.0f*sum2); +} + +/** + Calculate the moment of inertia for a solid box. +*/ +cpFloat +cpMomentForBox(cpFloat m, cpFloat width, cpFloat height) +{ + return m*(width*width + height*height)/12.0f; +} + +//#ifdef __cplusplus +//} +//#endif +// +//#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/chipmunk_types_h.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,159 @@ + +// written in the D programming language + +module chipmunkd.chipmunk_types_h; + +//#ifdef __APPLE__ +// #import "TargetConditionals.h" +//#endif +// +//#ifndef CP_USE_DOUBLES +// // Use single precision floats on the iPhone. +// #if TARGET_OS_IPHONE +// #define CP_USE_DOUBLES 0 +// #else +// // use doubles by default for higher precision +// #define CP_USE_DOUBLES 1 +// #endif +//#endif +// +version(CP_USE_DOUBLES){ + alias double cpFloat; + //TODO: +// #define cpfsqrt sqrt +// #define cpfsin sin +// #define cpfcos cos +// #define cpfacos acos +// #define cpfatan2 atan2 +// #define cpfmod fmod +// #define cpfexp exp +// #define cpfpow pow +// #define cpffloor floor +// #define cpfceil ceil +}else{ + extern (C) nothrow { + float sqrtf(float); + float sinf(float); + float cosf(float); + float acosf(float); + float atan2f(float,float); + float fmodf(float,float); + float expf(float); + float powf(float,float); + float floorf(float); + float ceilf(float); + } + + alias float cpFloat; + + alias sqrtf cpfsqrt; + alias sinf cpfsin; + alias cosf cpfcos; + alias acosf cpfacos; + alias atan2f cpfatan2; + alias fmodf cpfmod; + alias expf cpfexp; + alias powf cpfpow; + alias floorf cpffloor; + alias ceilf cpfceil; +} + +static cpFloat +cpfmax(cpFloat a, cpFloat b) +{ + return (a > b) ? a : b; +} + +static cpFloat +cpfmin(cpFloat a, cpFloat b) +{ + return (a < b) ? a : b; +} + +static cpFloat +cpfabs(cpFloat n) +{ + return (n < 0) ? -n : n; +} + +static cpFloat +cpfclamp(cpFloat f, cpFloat min, cpFloat max) +{ + return cpfmin(cpfmax(f, min), max); +} + +static cpFloat +cpflerp(cpFloat f1, cpFloat f2, cpFloat t) +{ + return f1*(1.0f - t) + f2*t; +} + +static cpFloat +cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d) +{ + return f1 + cpfclamp(f2 - f1, -d, d); +} + +//#if TARGET_OS_IPHONE +// // CGPoints are structurally the same, and allow +// // easy interoperability with other iPhone libraries +// #import <CoreGraphics/CGGeometry.h> +// typedef CGPoint cpVect; +//#else + struct cpVect{cpFloat x = 0; cpFloat y=0;} +//#endif + +alias uint cpHashValue; + +// Oh C, how we love to define our own boolean types to get compiler compatibility +//#ifdef CP_BOOL_TYPE +// typedef CP_BOOL_TYPE cpBool; +//#else + alias bool cpBool; +//#endif + +//#ifndef cpTrue + enum cpTrue = true; +//#endif + +//#ifndef cpFalse + enum cpFalse = false; +//#endif + +//#ifdef CP_DATA_POINTER_TYPE +// typedef CP_DATA_POINTER_TYPE cpDataPointer; +//#else + alias void * cpDataPointer; +//#endif + +//#ifdef CP_COLLISION_TYPE_TYPE +// typedef CP_COLLISION_TYPE_TYPE cpCollisionType; +//#else + alias uint cpCollisionType; +//#endif + +//#ifdef CP_GROUP_TYPE +// typedef CP_GROUP_TYPE cpGroup; +//#else + alias uint cpGroup; +//#endif + +//#ifdef CP_LAYERS_TYPE +// typedef CP_GROUP_TYPE cpLayers; +//#else + alias uint cpLayers; +//#endif + +//#ifdef CP_TIMESTAMP_TYPE +// typedef CP_TIMESTAMP_TYPE cpTimestamp; +//#else + alias uint cpTimestamp; +//#endif + +//#ifndef CP_NO_GROUP + enum CP_NO_GROUP = 0; +//#endif + +//#ifndef CP_ALL_LAYERS + enum CP_ALL_LAYERS = ~0; +//#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/constraints/cpConstraint.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,146 @@ + +// written in the D programming language + +module chipmunkd.constraints.cpConstraint; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.cpBody; + +alias void function(cpConstraint *constraint, cpFloat dt, cpFloat dt_inv) cpConstraintPreStepFunction; +alias void function(cpConstraint *constraint) cpConstraintApplyImpulseFunction; +alias cpFloat function(cpConstraint *constraint) cpConstraintGetImpulseFunction; + +struct cpConstraintClass { + cpConstraintPreStepFunction preStep; + cpConstraintApplyImpulseFunction applyImpulse; + cpConstraintGetImpulseFunction getImpulse; +} + + + +struct cpConstraint { + cpConstraintClass *klass; + + cpBody *a; + cpBody *b; + cpFloat maxForce; + cpFloat biasCoef; + cpFloat maxBias; + + cpDataPointer data; +} + +//#ifdef CP_USE_DEPRECATED_API_4 +//typedef cpConstraint cpJoint; +//#endif + +//void cpConstraintDestroy(cpConstraint *constraint); +//void cpConstraintFree(cpConstraint *constraint); + +static void +cpConstraintActivateBodies(cpConstraint *constraint) +{ + cpBody *a = constraint.a; if(a) cpBodyActivate(a); + cpBody *b = constraint.b; if(b) cpBodyActivate(b); +} + +static cpFloat +cpConstraintGetImpulse(cpConstraint *constraint) +{ + return constraint.klass.getImpulse(constraint); +} + +template cpConstraintCheckCast(string constraint, string _struct) +{ + enum cpConstraintCheckCast = + "assert("~constraint~".klass == "~_struct~"GetClass(), \"Constraint is not a "~_struct~"\");"; +} + +//#define cpConstraintCheckCast(constraint, struct) \ +// cpAssert(constraint->klass == struct##GetClass(), "Constraint is not a "#struct); + +template CP_DefineConstraintGetter(string _struct,string type,string member,string name) +{ + enum CP_DefineConstraintGetter = + "static "~type~" "~_struct~"Get"~name~"(cpConstraint *constraint){"~ + cpConstraintCheckCast!("constraint",_struct)~ + "cpConstraintActivateBodies(constraint);"~ + "return (cast("~_struct~"*)constraint)."~member~";"~ + "}"; +} + +//#define CP_DefineConstraintGetter(struct, type, member, name) \ +//static inline type \ +//struct##Get##name(const cpConstraint *constraint){ \ +// cpConstraintCheckCast(constraint, struct); \ +// return ((struct *)constraint)->member; \ +//} \ + +template CP_DefineConstraintSetter(string _struct,string type,string member,string name) +{ + enum CP_DefineConstraintSetter = + "static void "~_struct~"Set"~name~"(cpConstraint *constraint,"~type~" value){"~ + cpConstraintCheckCast!("constraint",_struct)~ + "cpConstraintActivateBodies(constraint);"~ + "(cast("~_struct~"*)constraint)."~member~" = value;"~ + "}"; +} + +//#define CP_DefineConstraintSetter(struct, type, member, name) \ +//static inline void \ +//struct##Set##name(cpConstraint *constraint, type value){ \ +// cpConstraintCheckCast(constraint, struct); \ +// cpConstraintActivateBodies(constraint); \ +// ((struct *)constraint)->member = value; \ +//} \ + +template CP_DefineConstraintProperty(string _struct,string type,string member,string name) +{ + enum CP_DefineConstraintProperty = + CP_DefineConstraintGetter!(_struct,type,member,name)~CP_DefineConstraintSetter!(_struct,type,member,name); + +} + +//#define CP_DefineConstraintProperty(struct, type, member, name) \ +//CP_DefineConstraintGetter(struct, type, member, name) \ +//CP_DefineConstraintSetter(struct, type, member, name) +//TODO: +//// Built in Joint types +public import chipmunkd.constraints.cpPinJoint; +//#include "cpSlideJoint.h" +public import chipmunkd.constraints.cpPivotJoint; +//#include "cpGrooveJoint.h" +public import chipmunkd.constraints.cpDampedSpring; +//#include "cpDampedRotarySpring.h" +//#include "cpRotaryLimitJoint.h" +//#include "cpRatchetJoint.h" +//#include "cpGearJoint.h" +//#include "cpSimpleMotor.h" + + +cpFloat cp_constraint_bias_coef = 0.1f; + +void cpConstraintDestroy(cpConstraint *constraint){} + +void +cpConstraintFree(cpConstraint *constraint) +{ + if(constraint){ + cpConstraintDestroy(constraint); + cpfree(constraint); + } +} + +void +cpConstraintInit(cpConstraint *constraint, /+const+/ cpConstraintClass *klass, cpBody *a, cpBody *b) +{ + constraint.klass = klass; + constraint.a = a; + constraint.b = b; + + constraint.maxForce = cast(cpFloat)INFINITY; + constraint.biasCoef = cp_constraint_bias_coef; + constraint.maxBias = cast(cpFloat)INFINITY; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/constraints/cpDampedSpring.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,127 @@ + +// written in the D programming language + +module chipmunkd.constraints.cpDampedSpring; + +import chipmunkd.chipmunk; +import chipmunkd.constraints.util; + +alias cpFloat function(cpConstraint *spring, cpFloat dist) cpDampedSpringForceFunc; + +//const cpConstraintClass *cpDampedSpringGetClass(); + +struct cpDampedSpring { + cpConstraint constraint; + cpVect anchr1, anchr2; + cpFloat restLength; + cpFloat stiffness; + cpFloat damping; + cpDampedSpringForceFunc springForceFunc; + + cpFloat target_vrn; + cpFloat v_coef; + + cpVect r1, r2; + cpFloat nMass; + cpVect n; +} + +//cpDampedSpring *cpDampedSpringAlloc(void); +//cpDampedSpring *cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiffness, cpFloat damping); +//cpConstraint *cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiffness, cpFloat damping); + +mixin(CP_DefineConstraintProperty!("cpDampedSpring", "cpVect", "anchr1", "Anchr1")); +mixin(CP_DefineConstraintProperty!("cpDampedSpring", "cpVect", "anchr2", "Anchr2")); +mixin(CP_DefineConstraintProperty!("cpDampedSpring", "cpFloat", "restLength", "RestLength")); +mixin(CP_DefineConstraintProperty!("cpDampedSpring", "cpFloat", "stiffness", "Stiffness")); +mixin(CP_DefineConstraintProperty!("cpDampedSpring", "cpFloat", "damping", "Damping")); +mixin(CP_DefineConstraintProperty!("cpDampedSpring", "cpDampedSpringForceFunc", "springForceFunc", "SpringForceFunc")); + +static cpFloat +defaultSpringForce(cpDampedSpring *spring, cpFloat dist){ + return (spring.restLength - dist)*spring.stiffness; +} + +static void +preStep(cpDampedSpring *spring, cpFloat dt, cpFloat dt_inv) +{ + mixin(CONSTRAINT_BEGIN!("spring", "a", "b")); + + spring.r1 = cpvrotate(spring.anchr1, a.rot); + spring.r2 = cpvrotate(spring.anchr2, b.rot); + + cpVect delta = cpvsub(cpvadd(b.p, spring.r2), cpvadd(a.p, spring.r1)); + cpFloat dist = cpvlength(delta); + spring.n = cpvmult(delta, 1.0f/(dist ? dist : INFINITY)); + + cpFloat k = k_scalar(a, b, spring.r1, spring.r2, spring.n); + spring.nMass = 1.0f/k; + + spring.target_vrn = 0.0f; + spring.v_coef = 1.0f - cpfexp(-spring.damping*dt*k); + + // apply spring force + cpFloat f_spring = spring.springForceFunc(cast(cpConstraint *)spring, dist); + apply_impulses(a, b, spring.r1, spring.r2, cpvmult(spring.n, f_spring*dt)); +} + +static void +applyImpulse(cpDampedSpring *spring) +{ + mixin(CONSTRAINT_BEGIN!("spring", "a", "b")); + + cpVect n = spring.n; + cpVect r1 = spring.r1; + cpVect r2 = spring.r2; + + // compute relative velocity + cpFloat vrn = normal_relative_velocity(a, b, r1, r2, n) - spring.target_vrn; + + // compute velocity loss from drag + // not 100% certain this is derived correctly, though it makes sense + cpFloat v_damp = -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)); +} + +static cpFloat +getImpulse(cpConstraint *constraint) +{ + return 0.0f; +} + +static /+const+/ cpConstraintClass klass = { + cast(cpConstraintPreStepFunction)&preStep, + cast(cpConstraintApplyImpulseFunction)&applyImpulse, + cast(cpConstraintGetImpulseFunction)&getImpulse, +}; +mixin(CP_DefineClassGetter!("cpDampedSpring")); + +cpDampedSpring * +cpDampedSpringAlloc() +{ + return cast(cpDampedSpring *)cpmalloc(cpDampedSpring.sizeof); +} + +cpDampedSpring * +cpDampedSpringInit(cpDampedSpring *spring, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiffness, cpFloat damping) +{ + cpConstraintInit(cast(cpConstraint *)spring, cpDampedSpringGetClass(), a, b); + + spring.anchr1 = anchr1; + spring.anchr2 = anchr2; + + spring.restLength = restLength; + spring.stiffness = stiffness; + spring.damping = damping; + spring.springForceFunc = cast(cpDampedSpringForceFunc)&defaultSpringForce; + + return spring; +} + +cpConstraint * +cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiffness, cpFloat damping) +{ + return cast(cpConstraint *)cpDampedSpringInit(cpDampedSpringAlloc(), a, b, anchr1, anchr2, restLength, stiffness, damping); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/constraints/cpPinJoint.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,124 @@ + +// written in the D programming language + +module chipmunkd.constraints.cpPinJoint; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.cpBody; +import chipmunkd.constraints.util; + +//const cpConstraintClass *cpPinJointGetClass(); + +struct cpPinJoint { + cpConstraint constraint; + cpVect anchr1, anchr2; + cpFloat dist; + + cpVect r1, r2; + cpVect n; + cpFloat nMass; + + cpFloat jnAcc, jnMax; + cpFloat bias; +} + +//cpPinJoint *cpPinJointAlloc(void); +//cpPinJoint *cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2); +//cpConstraint *cpPinJointNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2); +// +//CP_DefineConstraintProperty(cpPinJoint, cpVect, anchr1, Anchr1); +//CP_DefineConstraintProperty(cpPinJoint, cpVect, anchr2, Anchr2); +//CP_DefineConstraintProperty(cpPinJoint, cpFloat, dist, Dist); + +// cpPinJoint.c ------------------------- + +static void +preStep(cpPinJoint *joint, cpFloat dt, cpFloat dt_inv) +{ + mixin(CONSTRAINT_BEGIN!("joint", "a", "b")); + + joint.r1 = cpvrotate(joint.anchr1, a.rot); + joint.r2 = cpvrotate(joint.anchr2, b.rot); + + cpVect delta = cpvsub(cpvadd(b.p, joint.r2), cpvadd(a.p, joint.r1)); + cpFloat dist = cpvlength(delta); + joint.n = cpvmult(delta, 1.0f/(dist ? dist : cast(cpFloat)INFINITY)); + + // calculate mass normal + joint.nMass = 1.0f/k_scalar(a, b, joint.r1, joint.r2, joint.n); + + // calculate bias velocity + cpFloat maxBias = joint.constraint.maxBias; + joint.bias = cpfclamp(-joint.constraint.biasCoef*dt_inv*(dist - joint.dist), -maxBias, maxBias); + + // compute max impulse + joint.jnMax = mixin(J_MAX!("joint", "dt")); + + // apply accumulated impulse + cpVect j = cpvmult(joint.n, joint.jnAcc); + apply_impulses(a, b, joint.r1, joint.r2, j); +} + +static void +applyImpulse(cpPinJoint *joint) +{ + mixin(CONSTRAINT_BEGIN!("joint", "a", "b")); + cpVect n = joint.n; + + // compute relative velocity + cpFloat vrn = normal_relative_velocity(a, b, joint.r1, joint.r2, n); + + // compute normal impulse + cpFloat jn = (joint.bias - vrn)*joint.nMass; + cpFloat jnOld = joint.jnAcc; + joint.jnAcc = cpfclamp(jnOld + jn, -joint.jnMax, joint.jnMax); + jn = joint.jnAcc - jnOld; + + // apply impulse + apply_impulses(a, b, joint.r1, joint.r2, cpvmult(n, jn)); +} + +static cpFloat +getImpulse(cpPinJoint *joint) +{ + return cpfabs(joint.jnAcc); +} + +static /+const+/ cpConstraintClass klass = { + cast(cpConstraintPreStepFunction)&preStep, + cast(cpConstraintApplyImpulseFunction)&applyImpulse, + cast(cpConstraintGetImpulseFunction)&getImpulse, +}; +mixin(CP_DefineClassGetter!("cpPinJoint")); + +cpPinJoint * +cpPinJointAlloc() +{ + return cast(cpPinJoint *)cpmalloc(cpPinJoint.sizeof); +} + +cpPinJoint * +cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) +{ + cpConstraintInit(cast(cpConstraint *)joint, &klass, a, b); + + joint.anchr1 = anchr1; + joint.anchr2 = anchr2; + + // STATIC_BODY_CHECK + cpVect p1 = (a ? cpvadd(a.p, cpvrotate(anchr1, a.rot)) : anchr1); + cpVect p2 = (b ? cpvadd(b.p, cpvrotate(anchr2, b.rot)) : anchr2); + joint.dist = cpvlength(cpvsub(p2, p1)); + + joint.jnAcc = 0.0f; + + return joint; +} + +cpConstraint * +cpPinJointNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) +{ + return cast(cpConstraint *)cpPinJointInit(cpPinJointAlloc(), a, b, anchr1, anchr2); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/constraints/cpPivotJoint.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,118 @@ + +// written in the D programming language + +module chipmunkd.constraints.cpPivotJoint; + +import chipmunkd.chipmunk; +import chipmunkd.constraints.util; + +//const cpConstraintClass *cpPivotJointGetClass(); + +struct cpPivotJoint { + cpConstraint constraint; + cpVect anchr1, anchr2; + + cpVect r1, r2; + cpVect k1, k2; + + cpVect jAcc; + cpFloat jMaxLen; + cpVect bias; +} + +//cpPivotJoint *cpPivotJointAlloc(void); +//cpPivotJoint *cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2); +//cpConstraint *cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot); +//cpConstraint *cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2); +// +//CP_DefineConstraintProperty(cpPivotJoint, cpVect, anchr1, Anchr1); +//CP_DefineConstraintProperty(cpPivotJoint, cpVect, anchr2, Anchr2); + +static void +preStep(cpPivotJoint *joint, cpFloat dt, cpFloat dt_inv) +{ + mixin(CONSTRAINT_BEGIN!("joint", "a", "b")); + + joint.r1 = cpvrotate(joint.anchr1, a.rot); + joint.r2 = cpvrotate(joint.anchr2, b.rot); + + // Calculate mass tensor + k_tensor(a, b, joint.r1, joint.r2, &joint.k1, &joint.k2); + + // compute max impulse + joint.jMaxLen = mixin(J_MAX!("joint", "dt")); + + // calculate bias velocity + cpVect delta = cpvsub(cpvadd(b.p, joint.r2), cpvadd(a.p, joint.r1)); + joint.bias = cpvclamp(cpvmult(delta, -joint.constraint.biasCoef*dt_inv), joint.constraint.maxBias); + + // apply accumulated impulse + apply_impulses(a, b, joint.r1, joint.r2, joint.jAcc); +} + +static void +applyImpulse(cpPivotJoint *joint) +{ + mixin(CONSTRAINT_BEGIN!("joint", "a", "b")); + + cpVect r1 = joint.r1; + cpVect r2 = joint.r2; + + // compute relative velocity + cpVect vr = relative_velocity(a, b, r1, r2); + + // compute normal impulse + cpVect j = mult_k(cpvsub(joint.bias, vr), joint.k1, joint.k2); + cpVect jOld = joint.jAcc; + joint.jAcc = cpvclamp(cpvadd(joint.jAcc, j), joint.jMaxLen); + j = cpvsub(joint.jAcc, jOld); + + // apply impulse + apply_impulses(a, b, joint.r1, joint.r2, j); +} + +static cpFloat +getImpulse(cpConstraint *joint) +{ + return cpvlength((cast(cpPivotJoint *)joint).jAcc); +} + +static /+const+/ cpConstraintClass klass = { + cast(cpConstraintPreStepFunction)&preStep, + cast(cpConstraintApplyImpulseFunction)&applyImpulse, + cast(cpConstraintGetImpulseFunction)&getImpulse, +}; +mixin(CP_DefineClassGetter!("cpPivotJoint")); + +cpPivotJoint * +cpPivotJointAlloc() +{ + return cast(cpPivotJoint *)cpmalloc(cpPivotJoint.sizeof); +} + +cpPivotJoint * +cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) +{ + cpConstraintInit(cast(cpConstraint *)joint, &klass, a, b); + + joint.anchr1 = anchr1; + joint.anchr2 = anchr2; + + joint.jAcc = cpvzero; + + return joint; +} + +cpConstraint * +cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) +{ + return cast(cpConstraint *)cpPivotJointInit(cpPivotJointAlloc(), a, b, anchr1, anchr2); +} + +cpConstraint * +cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot) +{ + cpVect anchr1 = (a ? cpBodyWorld2Local(a, pivot) : pivot); + cpVect anchr2 = (b ? cpBodyWorld2Local(b, pivot) : pivot); + return cpPivotJointNew2(a, b, anchr1, anchr2); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/constraints/util.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,128 @@ + +// written in the D programming language + +module chipmunkd.constraints.util; + +import chipmunkd.cpBody; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; + +template CP_DefineClassGetter(string t) +{ + enum CP_DefineClassGetter = + "cpConstraintClass* "~t~"GetClass(){return cast(cpConstraintClass *)&klass;}"; +} + +template J_MAX(string constraint,string dt) +{ + enum J_MAX = "((cast(cpConstraint *)"~constraint~").maxForce*("~dt~"))"; +} + +template CONSTRAINT_BEGIN(string constraint,string a_var,string b_var) +{ + enum CONSTRAINT_BEGIN = "cpBody *"~a_var~"; cpBody *"~b_var~"; + "~a_var~" = (cast(cpConstraint *)"~constraint~")."~a_var~"; + "~b_var~" = (cast(cpConstraint *)"~constraint~")."~b_var~"; + if( + (cpBodyIsSleeping("~a_var~") || cpBodyIsStatic("~a_var~")) && + (cpBodyIsSleeping("~b_var~") || cpBodyIsStatic("~b_var~")) + ) return; + "; +} + +static cpVect +relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2){ + cpVect v1_sum = cpvadd(a.v, cpvmult(cpvperp(r1), a.w)); + cpVect v2_sum = cpvadd(b.v, cpvmult(cpvperp(r2), b.w)); + + return cpvsub(v2_sum, v1_sum); +} + +static cpFloat +normal_relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n){ + return cpvdot(relative_velocity(a, b, r1, r2), n); +} + +static void +apply_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j) +{ + cpBodyApplyImpulse(a, cpvneg(j), r1); + cpBodyApplyImpulse(b, j, r2); +} + +static void +apply_bias_impulse(cpBody *_body, cpVect j, cpVect r) +{ + _body.v_bias = cpvadd(_body.v_bias, cpvmult(j, _body.m_inv)); + _body.w_bias += _body.i_inv*cpvcross(r, j); +} + +static void +apply_bias_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j) +{ + apply_bias_impulse(a, cpvneg(j), r1); + apply_bias_impulse(b, j, r2); +} + +static cpVect +clamp_vect(cpVect v, cpFloat len) +{ + return cpvclamp(v, len); +// return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v; +} + +static cpFloat +k_scalar(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n) +{ + cpFloat mass_sum = a.m_inv + b.m_inv; + cpFloat r1cn = cpvcross(r1, n); + cpFloat r2cn = cpvcross(r2, n); + + cpFloat value = mass_sum + a.i_inv*r1cn*r1cn + b.i_inv*r2cn*r2cn; + assert(value != 0.0, "Unsolvable collision or constraint."); + + return value; +} + +static void +k_tensor(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect *k1, cpVect *k2) +{ + // calculate mass matrix + // If I wasn't lazy and wrote a proper matrix class, this wouldn't be so gross... + cpFloat k11, k12, k21, k22; + cpFloat m_sum = a.m_inv + b.m_inv; + + // start with I*m_sum + k11 = m_sum; k12 = 0.0f; + k21 = 0.0f; k22 = m_sum; + + // add the influence from r1 + cpFloat a_i_inv = a.i_inv; + cpFloat r1xsq = r1.x * r1.x * a_i_inv; + cpFloat r1ysq = r1.y * r1.y * a_i_inv; + cpFloat r1nxy = -r1.x * r1.y * a_i_inv; + k11 += r1ysq; k12 += r1nxy; + k21 += r1nxy; k22 += r1xsq; + + // add the influnce from r2 + cpFloat b_i_inv = b.i_inv; + cpFloat r2xsq = r2.x * r2.x * b_i_inv; + cpFloat r2ysq = r2.y * r2.y * b_i_inv; + cpFloat r2nxy = -r2.x * r2.y * b_i_inv; + k11 += r2ysq; k12 += r2nxy; + k21 += r2nxy; k22 += r2xsq; + + // invert + cpFloat determinant = k11*k22 - k12*k21; + assert(determinant != 0.0, "Unsolvable constraint."); + + cpFloat det_inv = 1.0f/determinant; + *k1 = cpv( k22*det_inv, -k12*det_inv); + *k2 = cpv(-k21*det_inv, k11*det_inv); +} + +static cpVect +mult_k(cpVect vr, cpVect k1, cpVect k2) +{ + return cpv(cpvdot(vr, k1), cpvdot(vr, k2)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpArbiter.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,394 @@ + +// written in the D programming language + +module chipmunkd.cpArbiter; + +import chipmunkd.cpSpace; +import chipmunkd.cpBody; +import chipmunkd.cpShape; +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.constraints.util; + +// Determines how fast penetrations resolve themselves. +//extern cpFloat cp_bias_coef; +// Amount of allowed penetration. Used to reduce vibrating contacts. +//extern cpFloat cp_collision_slop; + +// Data structure for contact points. +struct cpContact { + // Contact point and normal. + cpVect p, n; + // Penetration distance. + cpFloat dist; + + // Calculated by cpArbiterPreStep(). + cpVect r1, r2; + cpFloat nMass, tMass, bounce; + + // Persistant contact information. + cpFloat jnAcc, jtAcc, jBias; + cpFloat bias; + + // Hash value used to (mostly) uniquely identify a contact. + cpHashValue hash; +} + +//// Contacts are always allocated in groups. +//cpContact* cpContactInit(cpContact *con, cpVect p, cpVect n, cpFloat dist, cpHashValue hash); +// +//// Sum the contact impulses. (Can be used after cpSpaceStep() returns) +//cpVect cpContactsSumImpulses(cpContact *contacts, int numContacts); +//cpVect cpContactsSumImpulsesWithFriction(cpContact *contacts, int numContacts); + +enum cpArbiterState { + cpArbiterStateNormal, + cpArbiterStateFirstColl, + cpArbiterStateIgnore, + cpArbiterStateSleep, + cpArbiterStateCached, +} + +// Data structure for tracking collisions between shapes. +struct cpArbiter { + // Information on the contact points between the objects. + int numContacts; + cpContact *contacts; + + // The two shapes and bodies involved in the collision. + // These variables are NOT in the order defined by the collision handler. + // Using CP_ARBITER_GET_SHAPES and CP_ARBITER_GET_BODIES will save you from + // many headaches + cpShape* private_a; + cpShape* private_b; + + // Calculated before calling the pre-solve collision handler + // Override them with custom values if you want specialized behavior + cpFloat e; + cpFloat u; + // Used for surface_v calculations, implementation may change + cpVect surface_vr; + + // Time stamp of the arbiter. (from cpSpace) + cpTimestamp stamp; + + cpCollisionHandler *handler; + + // Are the shapes swapped in relation to the collision handler? + cpBool swappedColl; + cpArbiterState state; +} + +//// Arbiters are allocated in large buffers by the space and don't require a destroy function +//cpArbiter* cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b); +// +//// These functions are all intended to be used internally. +//// Inject new contact points into the arbiter while preserving contact history. +//void cpArbiterUpdate(cpArbiter *arb, cpContact *contacts, int numContacts, struct cpCollisionHandler *handler, cpShape *a, cpShape *b); +//// Precalculate values used by the solver. +//void cpArbiterPreStep(cpArbiter *arb, cpFloat dt_inv); +//void cpArbiterApplyCachedImpulse(cpArbiter *arb); +//// Run an iteration of the solver on the arbiter. +//void cpArbiterApplyImpulse(cpArbiter *arb, cpFloat eCoef); +// +//// Arbiter Helper Functions +//cpVect cpArbiterTotalImpulse(cpArbiter *arb); +//cpVect cpArbiterTotalImpulseWithFriction(cpArbiter *arb); +//void cpArbiterIgnore(cpArbiter *arb); + + +static void +cpArbiterGetShapes(/+const+/ cpArbiter *arb, cpShape **a, cpShape **b) +{ + if(arb.swappedColl){ + (*a) = arb.private_b, (*b) = arb.private_a; + } else { + (*a) = arb.private_a, (*b) = arb.private_b; + } +} +//#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b); +template CP_ARBITER_GET_SHAPES(string arb,string a,string b) +{ + enum CP_ARBITER_GET_SHAPES = "cpShape* "~a~", "~b~";"~ + "cpArbiterGetShapes("~arb~", &"~a~", &"~b~");"; +} + +static void +cpArbiterGetBodies(/+const+/ cpArbiter *arb, cpBody **a, cpBody **b) +{ + //CP_ARBITER_GET_SHAPES(arb, shape_a, shape_b); + cpShape *shape_a, shape_b; cpArbiterGetShapes(arb, &shape_a, &shape_b); + (*a) = shape_a._body; + (*b) = shape_b._body; +} +//#define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b); + +static cpBool +cpArbiterIsFirstContact(const cpArbiter *arb) +{ + return arb.state == cpArbiterState.cpArbiterStateFirstColl; +} + +static cpVect +cpArbiterGetNormal(const cpArbiter *arb, int i) +{ + cpVect n = arb.contacts[i].n; + return arb.swappedColl ? cpvneg(n) : n; +} + +static cpVect +cpArbiterGetPoint(const cpArbiter *arb, int i) +{ + return arb.contacts[i].p; +} + +cpFloat cp_bias_coef = 0.1f; +cpFloat cp_collision_slop = 0.1f; + +cpContact* +cpContactInit(cpContact *con, cpVect p, cpVect n, cpFloat dist, cpHashValue hash) +{ + con.p = p; + con.n = n; + con.dist = dist; + + con.jnAcc = 0.0f; + con.jtAcc = 0.0f; + con.jBias = 0.0f; + + con.hash = hash; + + return con; +} + +cpVect +cpArbiterTotalImpulse(cpArbiter *arb) +{ + cpContact *contacts = arb.contacts; + cpVect sum = cpvzero; + + for(int i=0, count=arb.numContacts; i<count; i++){ + cpContact *con = &contacts[i]; + sum = cpvadd(sum, cpvmult(con.n, con.jnAcc)); + } + + return sum; +} + +cpVect +cpArbiterTotalImpulseWithFriction(cpArbiter *arb) +{ + cpContact *contacts = arb.contacts; + cpVect sum = cpvzero; + + for(int i=0, count=arb.numContacts; i<count; i++){ + cpContact *con = &contacts[i]; + sum = cpvadd(sum, cpvrotate(con.n, cpv(con.jnAcc, con.jtAcc))); + } + + 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 = cpArbiterState.cpArbiterStateIgnore; +} + +cpArbiter* +cpArbiterAlloc() +{ + return cast(cpArbiter *)cpcalloc(1, cpArbiter.sizeof); +} + +cpArbiter* +cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b) +{ + arb.numContacts = 0; + arb.contacts = null; + + arb.private_a = a; + arb.private_b = b; + + arb.stamp = 0; + arb.state = cpArbiterState.cpArbiterStateFirstColl; + + return arb; +} + +cpArbiter* +cpArbiterNew(cpShape *a, cpShape *b) +{ + return cpArbiterInit(cpArbiterAlloc(), a, b); +} + +void +cpArbiterDestroy(cpArbiter *arb) +{ +// if(arb.contacts) cpfree(arb.contacts); +} + +void +cpArbiterFree(cpArbiter *arb) +{ + if(arb){ + cpArbiterDestroy(arb); + cpfree(arb); + } +} + +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]; + + 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; + } + } + } + +// cpfree(arb.contacts); + } + + arb.contacts = contacts; + arb.numContacts = numContacts; + + arb.handler = handler; + arb.swappedColl = (a.collision_type != handler.a); + + arb.e = a.e * b.e; + arb.u = a.u * b.u; + arb.surface_vr = cpvsub(a.surface_v, b.surface_v); + + // For collisions between two similar primitive types, the order could have been swapped. + arb.private_a = a; + arb.private_b = b; + + // mark it as new if it's been cached + if(arb.state == cpArbiterState.cpArbiterStateCached) arb.state = cpArbiterState.cpArbiterStateFirstColl; +} + +void +cpArbiterPreStep(cpArbiter *arb, cpFloat dt_inv) +{ + cpBody *a = arb.private_a._body; + cpBody *b = arb.private_b._body; + + for(int i=0; i<arb.numContacts; i++){ + cpContact *con = &arb.contacts[i]; + + // Calculate the offsets. + con.r1 = cpvsub(con.p, a.p); + con.r2 = cpvsub(con.p, b.p); + + // Calculate the mass normal and mass tangent. + con.nMass = 1.0f/k_scalar(a, b, con.r1, con.r2, con.n); + con.tMass = 1.0f/k_scalar(a, b, con.r1, con.r2, cpvperp(con.n)); + + // Calculate the target bias velocity. + con.bias = -cp_bias_coef*dt_inv*cpfmin(0.0f, con.dist + cp_collision_slop); + con.jBias = 0.0f; + + // Calculate the target bounce velocity. + con.bounce = normal_relative_velocity(a, b, con.r1, con.r2, con.n)*arb.e;//cpvdot(con.n, cpvsub(v2, v1))*e; + } +} + +void +cpArbiterApplyCachedImpulse(cpArbiter *arb) +{ + cpShape *shapea = arb.private_a; + cpShape *shapeb = arb.private_b; + + arb.u = shapea.u * shapeb.u; + arb.surface_vr = cpvsub(shapeb.surface_v, shapea.surface_v); + + cpBody *a = shapea._body; + cpBody *b = shapeb._body; + + for(int i=0; i<arb.numContacts; i++){ + cpContact *con = &arb.contacts[i]; + apply_impulses(a, b, con.r1, con.r2, cpvrotate(con.n, cpv(con.jnAcc, con.jtAcc))); + } +} + +void +cpArbiterApplyImpulse(cpArbiter *arb, cpFloat eCoef) +{ + cpBody *a = arb.private_a._body; + cpBody *b = arb.private_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))); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpArray.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,133 @@ + +// written in the D programming language + +module chipmunkd.cpArray; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; + +import core.stdc.string:memcpy; + +struct cpArray{ + int num, max; + void **arr; +} + +alias void function(void *ptr, void *data) cpArrayIter; + +//#define CP_ARRAY_INCREMENT 10 + +// NOTE: cpArray is rarely used and will probably go away. + +cpArray* +cpArrayAlloc() +{ + return cast(cpArray *)cpcalloc(1, cpArray.sizeof); +} + +cpArray* +cpArrayInit(cpArray *arr, int size) +{ + arr.num = 0; + + size = (size ? size : 4); + arr.max = size; + arr.arr = cast(void **)cpmalloc(size*(void**).sizeof); + + return arr; +} + +cpArray* +cpArrayNew(int size) +{ + return cpArrayInit(cpArrayAlloc(), size); +} + +void +cpArrayDestroy(cpArray *arr) +{ + cpfree(arr.arr); + arr.arr = null; +} + +void +cpArrayFree(cpArray *arr) +{ + if(arr){ + cpArrayDestroy(arr); + cpfree(arr); + } +} + +void +cpArrayPush(cpArray *arr, void *object) +{ + if(arr.num == arr.max){ + arr.max *= 2; + arr.arr = cast(void **)cprealloc(arr.arr, arr.max*(void**).sizeof); + } + + arr.arr[arr.num] = object; + arr.num++; +} + +void * +cpArrayPop(cpArray *arr) +{ + arr.num--; + + void *value = arr.arr[arr.num]; + arr.arr[arr.num] = null; + + return value; +} + +void +cpArrayDeleteIndex(cpArray *arr, int idx) +{ + arr.num--; + + arr.arr[idx] = arr.arr[arr.num]; + arr.arr[arr.num] = null; +} + +void +cpArrayDeleteObj(cpArray *arr, void *obj) +{ + for(int i=0; i<arr.num; i++){ + if(arr.arr[i] == obj){ + cpArrayDeleteIndex(arr, i); + return; + } + } +} + +void +cpArrayAppend(cpArray *arr, cpArray *other) +{ + void *tail = &arr.arr[arr.num]; + + arr.num += other.num; + if(arr.num >= arr.max){ + arr.max = arr.num; + arr.arr = cast(void **)cprealloc(arr.arr, arr.max*(void**).sizeof); + } + + memcpy(tail, other.arr, other.num*(void**).sizeof); +} + +void +cpArrayEach(cpArray *arr, cpArrayIter iterFunc, void *data) +{ + for(int i=0; i<arr.num; i++) + iterFunc(arr.arr[i], data); +} + +cpBool +cpArrayContains(cpArray *arr, void *ptr) +{ + for(int i=0; i<arr.num; i++) + if(arr.arr[i] == ptr) return cpTrue; + + return cpFalse; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpBB.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,79 @@ + +// written in the D programming language + +module chipmunkd.cpBB; + +import chipmunkd.cpVect_h; +import chipmunkd.chipmunk_types_h; + +struct cpBB{ + cpFloat l, b, r ,t; +} + +static cpBB +cpBBNew(const cpFloat l, const cpFloat b, + const cpFloat r, const cpFloat t) +{ + cpBB bb = {l, b, r, t}; + return bb; +} + +static cpBool +cpBBintersects(const cpBB a, const cpBB b) +{ + return (a.l<=b.r && b.l<=a.r && a.b<=b.t && b.b<=a.t); +} + +static cpBool +cpBBcontainsBB(const cpBB bb, const cpBB other) +{ + return (bb.l < other.l && bb.r > other.r && bb.b < other.b && bb.t > other.t); +} + +static cpBool +cpBBcontainsVect(const cpBB bb, const cpVect v) +{ + return (bb.l < v.x && bb.r > v.x && bb.b < v.y && bb.t > v.y); +} + +static cpBB +cpBBmerge(const cpBB a, const cpBB b){ + return cpBBNew( + cpfmin(a.l, b.l), + cpfmin(a.b, b.b), + cpfmax(a.r, b.r), + cpfmax(a.t, b.t) + ); +} + +static cpBB +cpBBexpand(const cpBB bb, const cpVect v){ + return cpBBNew( + cpfmin(bb.l, v.x), + cpfmin(bb.b, v.y), + cpfmax(bb.r, v.x), + cpfmax(bb.t, v.y) + ); +} + +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); +} + +cpVect +cpBBWrapVect(const cpBB bb, const cpVect v) +{ + cpFloat ix = cpfabs(bb.r - bb.l); + cpFloat modx = cpfmod(v.x - bb.l, ix); + cpFloat x = (modx > 0.0f) ? modx : modx + ix; + + cpFloat iy = cpfabs(bb.t - bb.b); + cpFloat mody = cpfmod(v.y - bb.b, iy); + cpFloat y = (mody > 0.0f) ? mody : mody + iy; + + return cpv(x + bb.l, y + bb.b); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpBody.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,367 @@ + +// written in the D programming language + +module chipmunkd.cpBody; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.cpShape; + +alias void function(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt)cpBodyVelocityFunc; +alias void function(cpBody *_body, cpFloat dt)cpBodyPositionFunc; + +//extern cpBodyVelocityFunc cpBodyUpdateVelocityDefault; +//extern cpBodyPositionFunc cpBodyUpdatePositionDefault; + +// Structure to hold information about the contact graph components +// when putting groups of objects to sleep. +// No interesting user accessible fields. +struct cpComponentNode { + cpBody *parent; + cpBody *next; + int rank; + cpFloat idleTime; +} + +struct cpBody{ + // *** Integration Functions. + + // Function that is called to integrate the body's velocity. (Defaults to cpBodyUpdateVelocity) + cpBodyVelocityFunc velocity_func; + + // Function that is called to integrate the body's position. (Defaults to cpBodyUpdatePosition) + cpBodyPositionFunc position_func; + + // *** Mass Properties + + // Mass and it's inverse. + // Always use cpBodySetMass() whenever changing the mass as these values must agree. + cpFloat m, m_inv; + + // Moment of inertia and it's inverse. + // Always use cpBodySetMoment() whenever changing the moment as these values must agree. + cpFloat i, i_inv; + + // *** Positional Properties + + // Linear components of motion (position, velocity, and force) + cpVect p, v, f; + + // Angular components of motion (angle, angular velocity, and torque) + // Always use cpBodySetAngle() to set the angle of the body as a and rot must agree. + cpFloat a, w, t; + + // Cached unit length vector representing the angle of the body. + // Used for fast vector rotation using cpvrotate(). + cpVect rot; + + // *** User Definable Fields + + // User defined data pointer. + cpDataPointer data; + + // *** Other Fields + + // Maximum velocities this body can move at after integrating velocity + cpFloat v_limit, w_limit; + + // *** Internally Used Fields + + // Velocity bias values used when solving penetrations and correcting constraints. + cpVect v_bias; + cpFloat w_bias; + + // Space this body has been added to + cpSpace* space; + + // Pointer to the shape list. + // Shapes form a linked list using cpShape.next when added to a space. + cpShape *shapesList; + + // Used by cpSpaceStep() to store contact graph information. + cpComponentNode node; +} + +//// Basic allocation/destruction functions +//cpBody *cpBodyAlloc(void); +//cpBody *cpBodyInit(cpBody *body, cpFloat m, cpFloat i); +//cpBody *cpBodyNew(cpFloat m, cpFloat i); +// +//void cpBodyDestroy(cpBody *body); +//void cpBodyFree(cpBody *body); +// +//// Wake up a sleeping or idle body. (defined in cpSpace.c) +//void cpBodyActivate(cpBody *body); +// +//// Force a body to sleep; +//void cpBodySleep(cpBody *body); +////void cpBodySleepGroup(cpBody *body, ...); + +static cpBool +cpBodyIsSleeping(const cpBody *_body) +{ + return (_body.node.next !is null); +} + +//cpBool cpBodyIsStatic(const cpBody *body); + +static cpBool +cpBodyIsRogue(const cpBody* _body) +{ + return (_body.space is null); +} + + +//#define CP_DefineBodyGetter(type, member, name) \ +//static inline type cpBodyGet##name(const cpBody *body){return body.member;} +// +//#define CP_DefineBodySetter(type, member, name) \ +//static inline void \ +//cpBodySet##name(cpBody *body, const type value){ \ +// cpBodyActivate(body); \ +// body.member = value; \ +//} \ + +//#define CP_DefineBodyProperty(type, member, name) \ +//CP_DefineBodyGetter(type, member, name) \ +//CP_DefineBodySetter(type, member, name) + + +//// Accessors for cpBody struct members +//CP_DefineBodyGetter(cpFloat, m, Mass); +//void cpBodySetMass(cpBody *body, cpFloat m); +// +//CP_DefineBodyGetter(cpFloat, i, Moment); +//void cpBodySetMoment(cpBody *body, cpFloat i); +// +// +//CP_DefineBodyProperty(cpVect, p, Pos); +//CP_DefineBodyProperty(cpVect, v, Vel); +//CP_DefineBodyProperty(cpVect, f, Force); +static cpFloat cpBodyGetAngle(const cpBody *_body){return _body.a;} +static cpFloat cpBodySetAngle(cpBody *_body, const cpFloat value){cpBodyActivate(_body); return _body.a = value;} +//CP_DefineBodyProperty(cpFloat, w, AngVel); +//CP_DefineBodyProperty(cpFloat, t, Torque); +//CP_DefineBodyGetter(cpVect, rot, Rot); +//CP_DefineBodyProperty(cpFloat, v_limit, VelLimit); +//CP_DefineBodyProperty(cpFloat, w_limit, AngVelLimit); + +//// Modify the velocity of the body so that it will move to the specified absolute coordinates in the next timestep. +//// Intended for objects that are moved manually with a custom velocity integration function. +//void cpBodySlew(cpBody *body, cpVect pos, cpFloat dt); +// +//// Default Integration functions. +//void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt); +//void cpBodyUpdatePosition(cpBody *body, cpFloat dt); + +// Convert body local to world coordinates +static cpVect +cpBodyLocal2World(const cpBody *_body, const cpVect v) +{ + return cpvadd(_body.p, cpvrotate(v, _body.rot)); +} + +// Convert world to body local coordinates +static cpVect +cpBodyWorld2Local(const cpBody *_body, const cpVect v) +{ + return cpvunrotate(cpvsub(v, _body.p), _body.rot); +} + +// Apply an impulse (in world coordinates) to the body at a point relative to the center of gravity (also in world coordinates). +static void +cpBodyApplyImpulse(cpBody *_body, const cpVect j, const cpVect r) +{ + _body.v = cpvadd(_body.v, cpvmult(j, _body.m_inv)); + _body.w += _body.i_inv*cpvcross(r, j); +} + +//// Zero the forces on a body. +//void cpBodyResetForces(cpBody *body); +//// Apply a force (in world coordinates) to a body at a point relative to the center of gravity (also in world coordinates). +//void cpBodyApplyForce(cpBody *body, const cpVect f, const cpVect r); + +static cpFloat +cpBodyKineticEnergy(const cpBody *_body) +{ + // Need to do some fudging to avoid NaNs + cpFloat vsq = cpvdot(_body.v, _body.v); + cpFloat wsq = _body.w*_body.w; + return (vsq ? vsq*_body.m : 0.0f) + (wsq ? wsq*_body.i : 0.0f); +} + +//// Apply a damped spring force between two bodies. +//// Warning: Large damping values can be unstable. Use a cpDampedSpring constraint for this instead. +//void cpApplyDampedSpring(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat rlen, cpFloat k, cpFloat dmp, cpFloat dt); + +// initialized in cpInitChipmunk() +cpBody cpStaticBodySingleton; + +cpBody* +cpBodyAlloc() +{ + return cast(cpBody *)cpmalloc(cpBody.sizeof); +} + +cpBodyVelocityFunc cpBodyUpdateVelocityDefault = &cpBodyUpdateVelocity; +cpBodyPositionFunc cpBodyUpdatePositionDefault = &cpBodyUpdatePosition; + +cpBody* +cpBodyInit(cpBody *_body, cpFloat m, cpFloat i) +{ + _body.velocity_func = cpBodyUpdateVelocityDefault; + _body.position_func = cpBodyUpdatePositionDefault; + + cpBodySetMass(_body, m); + cpBodySetMoment(_body, i); + + _body.p = cpvzero; + _body.v = cpvzero; + _body.f = cpvzero; + + cpBodySetAngle(_body, 0.0f); + _body.w = 0.0f; + _body.t = 0.0f; + + _body.v_bias = cpvzero; + _body.w_bias = 0.0f; + + _body.data = null; + _body.v_limit = INFINITY; + _body.w_limit = INFINITY; + + _body.space = null; + _body.shapesList = null; + + cpComponentNode node = {null, null, 0, 0.0f}; + _body.node = node; + + return _body; +} + +cpBody* +cpBodyNew(cpFloat m, cpFloat i) +{ + return cpBodyInit(cpBodyAlloc(), m, i); +} + +void cpBodyDestroy(cpBody *_body){} + +void +cpBodyFree(cpBody *_body) +{ + if(_body){ + cpBodyDestroy(_body); + cpfree(_body); + } +} + +void +cpBodySetMass(cpBody *_body, cpFloat mass) +{ + _body.m = mass; + _body.m_inv = 1.0f/mass; +} + +void +cpBodySetMoment(cpBody *_body, cpFloat moment) +{ + _body.i = moment; + _body.i_inv = 1.0f/moment; +} + +void +cpBodySetAngle(cpBody *_body, cpFloat angle) +{ + _body.a = angle;//fmod(a, (cpFloat)M_PI*2.0f); + _body.rot = cpvforangle(angle); +} + +void +cpBodySlew(cpBody *_body, cpVect pos, cpFloat dt) +{ + cpVect delta = cpvsub(pos, _body.p); + _body.v = cpvmult(delta, 1.0f/dt); +} + +void +cpBodyUpdateVelocity(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt) +{ + _body.v = cpvclamp( + cpvadd(cpvmult(_body.v, damping), cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), dt)), + _body.v_limit); + + cpFloat w_limit = _body.w_limit; + _body.w = cpfclamp(_body.w*damping + _body.t*_body.i_inv*dt, -w_limit, w_limit); +} + +void +cpBodyUpdatePosition(cpBody *_body, cpFloat dt) +{ + _body.p = cpvadd(_body.p, cpvmult(cpvadd(_body.v, _body.v_bias), dt)); + cpBodySetAngle(_body, _body.a + (_body.w + _body.w_bias)*dt); + + _body.v_bias = cpvzero; + _body.w_bias = 0.0f; +} + +void +cpBodyResetForces(cpBody *_body) +{ + _body.f = cpvzero; + _body.t = 0.0f; +} + +void +cpBodyApplyForce(cpBody *_body, cpVect force, cpVect r) +{ + _body.f = cpvadd(_body.f, force); + _body.t += cpvcross(r, force); +} + +void +cpApplyDampedSpring(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat rlen, cpFloat k, cpFloat dmp, cpFloat dt) +{ + // Calculate the world space anchor coordinates. + cpVect r1 = cpvrotate(anchr1, a.rot); + cpVect r2 = cpvrotate(anchr2, b.rot); + + cpVect delta = cpvsub(cpvadd(b.p, r2), cpvadd(a.p, r1)); + cpFloat dist = cpvlength(delta); + cpVect n = dist ? cpvmult(delta, 1.0f/dist) : cpvzero; + + cpFloat f_spring = (dist - rlen)*k; + + // Calculate the world relative velocities of the anchor points. + cpVect v1 = cpvadd(a.v, cpvmult(cpvperp(r1), a.w)); + cpVect v2 = cpvadd(b.v, cpvmult(cpvperp(r2), b.w)); + + // Calculate the damping force. + // This really should be in the impulse solver and can produce problems when using large damping values. + cpFloat vrn = cpvdot(cpvsub(v2, v1), n); + cpFloat f_damp = vrn*cpfmin(dmp, 1.0f/(dt*(a.m_inv + b.m_inv))); + + // Apply! + cpVect f = cpvmult(n, f_spring + f_damp); + cpBodyApplyForce(a, f, r1); + cpBodyApplyForce(b, cpvneg(f), r2); +} + +cpBool +cpBodyIsStatic(/+const+/ cpBody *_body) +{ + cpSpace *space = _body.space; + return ( (space !is null) && (_body is &space.staticBody) ); +} + +//void cpSpaceSleepBody(cpSpace *space, cpBody *_body); + +void +cpBodySleep(cpBody *_body) +{ + if(cpBodyIsSleeping(_body)) return; + + assert(!cpBodyIsStatic(_body) && !cpBodyIsRogue(_body), "Rogue and static bodies cannot be put to sleep."); + cpSpaceSleepBody(_body.space, _body); //TODO +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpCollision.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,380 @@ + +// written in the D programming language + +module chipmunkd.cpCollision; + +import chipmunkd.chipmunk_types_h; +import chipmunkd.chipmunk; +import chipmunkd.cpShape; + +alias int function(const cpShape *, const cpShape *, cpContact *) collisionFunc; + + +// Add contact points for circle to circle collisions. +// Used by several collision tests. +static int +circle2circleQuery(const cpVect p1, const cpVect p2, const cpFloat r1, const cpFloat r2, cpContact *con) +{ + cpFloat mindist = r1 + r2; + cpVect delta = cpvsub(p2, p1); + cpFloat distsq = cpvlengthsq(delta); + if(distsq >= mindist*mindist) return 0; + + cpFloat dist = cpfsqrt(distsq); + + // Allocate and initialize the contact. + cpContactInit( + con, + cpvadd(p1, cpvmult(delta, 0.5f + (r1 - 0.5f*mindist)/(dist ? dist : INFINITY))), + (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f)), + dist - mindist, + 0 + ); + + return 1; +} + +// Collide circle shapes. +static int +circle2circle(const cpShape *shape1, const cpShape *shape2, cpContact *arr) +{ + cpCircleShape *circ1 = cast(cpCircleShape*)shape1; + cpCircleShape *circ2 = cast(cpCircleShape*)shape2; + + return circle2circleQuery(circ1.tc, circ2.tc, circ1.r, circ2.r, arr); +} + +// Collide circles to segment shapes. +static int +circle2segment(const cpShape *circleShape, const cpShape *segmentShape, cpContact *con) +{ + cpCircleShape *circ = cast(cpCircleShape *)circleShape; + cpSegmentShape *seg = cast(cpSegmentShape *)segmentShape; + + // Radius sum + cpFloat rsum = circ.r + seg.r; + + // Calculate normal distance from segment. + cpFloat dn = cpvdot(seg.tn, circ.tc) - cpvdot(seg.ta, seg.tn); + cpFloat dist = cpfabs(dn) - rsum; + if(dist > 0.0f) return 0; + + // Calculate tangential distance along segment. + cpFloat dt = -cpvcross(seg.tn, circ.tc); + cpFloat dtMin = -cpvcross(seg.tn, seg.ta); + cpFloat dtMax = -cpvcross(seg.tn, seg.tb); + + // Decision tree to decide which feature of the segment to collide with. + if(dt < dtMin){ + if(dt < (dtMin - rsum)){ + return 0; + } else { + return circle2circleQuery(circ.tc, seg.ta, circ.r, seg.r, con); + } + } else { + if(dt < dtMax){ + cpVect n = (dn < 0.0f) ? seg.tn : cpvneg(seg.tn); + cpContactInit( + con, + cpvadd(circ.tc, cpvmult(n, circ.r + dist*0.5f)), + n, + dist, + 0 + ); + return 1; + } else { + if(dt < (dtMax + rsum)) { + return circle2circleQuery(circ.tc, seg.tb, circ.r, seg.r, con); + } else { + return 0; + } + } + } + + return 1; +} + +// Helper function for working with contact buffers +// This used to malloc/realloc memory on the fly but was repurposed. +static cpContact * +nextContactPoint(cpContact *arr, int *numPtr) +{ + int num = *numPtr; + + if(num < CP_MAX_CONTACTS_PER_ARBITER) + (*numPtr) = num + 1; + + return &arr[num]; +} + +// Find the minimum separating axis for the give poly and axis list. +static int +findMSA(const cpPolyShape *poly, const cpPolyShapeAxis *axes, const int num, cpFloat *min_out) +{ + int min_index = 0; + cpFloat min = cpPolyShapeValueOnAxis(poly, axes.n, axes.d); + if(min > 0.0f) return -1; + + for(int i=1; i<num; i++){ + cpFloat dist = cpPolyShapeValueOnAxis(poly, axes[i].n, axes[i].d); + if(dist > 0.0f) { + return -1; + } else if(dist > min){ + min = dist; + min_index = i; + } + } + + (*min_out) = min; + return min_index; +} + +// Add contacts for probably penetrating vertexes. +// This handles the degenerate case where an overlap was detected, but no vertexes fall inside +// the opposing polygon. (like a star of david) +static int +findVertsFallback(cpContact *arr, const cpPolyShape *poly1, const cpPolyShape *poly2, const cpVect n, const cpFloat dist) +{ + int num = 0; + + for(int i=0; i<poly1.numVerts; i++){ + cpVect v = poly1.tVerts[i]; + if(cpPolyShapeContainsVertPartial(poly2, v, cpvneg(n))) + cpContactInit(nextContactPoint(arr, &num), v, n, dist, CP_HASH_PAIR(poly1.shape.hashid, cast(const cpHashValue)i)); + } + + for(int i=0; i<poly2.numVerts; i++){ + cpVect v = poly2.tVerts[i]; + if(cpPolyShapeContainsVertPartial(poly1, v, n)) + cpContactInit(nextContactPoint(arr, &num), v, n, dist, CP_HASH_PAIR(poly2.shape.hashid, cast(const cpHashValue)i)); + } + + return num; +} + +// Add contacts for penetrating vertexes. +static int +findVerts(cpContact *arr, const cpPolyShape *poly1, const cpPolyShape *poly2, const cpVect n, const cpFloat dist) +{ + int num = 0; + + for(int i=0; i<poly1.numVerts; i++){ + cpVect v = poly1.tVerts[i]; + if(cpPolyShapeContainsVert(poly2, v)) + cpContactInit(nextContactPoint(arr, &num), v, n, dist, CP_HASH_PAIR(poly1.shape.hashid, cast(const cpHashValue)i)); + } + + for(int i=0; i<poly2.numVerts; i++){ + cpVect v = poly2.tVerts[i]; + if(cpPolyShapeContainsVert(poly1, v)) + cpContactInit(nextContactPoint(arr, &num), v, n, dist, CP_HASH_PAIR(poly2.shape.hashid, cast(const cpHashValue)i)); + } + + return (num ? num : findVertsFallback(arr, poly1, poly2, n, dist)); +} + +// Collide poly shapes together. +static int +poly2poly(const cpShape *shape1, const cpShape *shape2, cpContact *arr) +{ + cpPolyShape *poly1 = cast(cpPolyShape *)shape1; + cpPolyShape *poly2 = cast(cpPolyShape *)shape2; + + cpFloat min1; + int mini1 = findMSA(poly2, poly1.tAxes, poly1.numVerts, &min1); + if(mini1 == -1) return 0; + + cpFloat min2; + int mini2 = findMSA(poly1, poly2.tAxes, poly2.numVerts, &min2); + if(mini2 == -1) return 0; + + // There is overlap, find the penetrating verts + if(min1 > min2) + return findVerts(arr, poly1, poly2, poly1.tAxes[mini1].n, min1); + else + return findVerts(arr, poly1, poly2, cpvneg(poly2.tAxes[mini2].n), min2); +} + +// Like cpPolyValueOnAxis(), but for segments. +static cpFloat +segValueOnAxis(const cpSegmentShape *seg, const cpVect n, const cpFloat d) +{ + cpFloat a = cpvdot(n, seg.ta) - seg.r; + cpFloat b = cpvdot(n, seg.tb) - seg.r; + return cpfmin(a, b) - d; +} + +// Identify vertexes that have penetrated the segment. +static void +findPointsBehindSeg(cpContact *arr, int *num, const cpSegmentShape *seg, const cpPolyShape *poly, const cpFloat pDist, const cpFloat coef) +{ + cpFloat dta = cpvcross(seg.tn, seg.ta); + cpFloat dtb = cpvcross(seg.tn, seg.tb); + cpVect n = cpvmult(seg.tn, coef); + + for(int i=0; i<poly.numVerts; i++){ + cpVect v = poly.tVerts[i]; + if(cpvdot(v, n) < cpvdot(seg.tn, seg.ta)*coef + seg.r){ + cpFloat dt = cpvcross(seg.tn, v); + if(dta >= dt && dt >= dtb){ + cpContactInit(nextContactPoint(arr, num), v, n, pDist, CP_HASH_PAIR(poly.shape.hashid, cast(const cpHashValue)i)); + } + } + } +} + +// This one is complicated and gross. Just don't go there... +// TODO: Comment me! +static int +seg2poly(const cpShape *shape1, const cpShape *shape2, cpContact *arr) +{ + cpSegmentShape *seg = cast(cpSegmentShape *)shape1; + cpPolyShape *poly = cast(cpPolyShape *)shape2; + cpPolyShapeAxis *axes = poly.tAxes; + + cpFloat segD = cpvdot(seg.tn, seg.ta); + cpFloat minNorm = cpPolyShapeValueOnAxis(poly, seg.tn, segD) - seg.r; + cpFloat minNeg = cpPolyShapeValueOnAxis(poly, cpvneg(seg.tn), -segD) - seg.r; + if(minNeg > 0.0f || minNorm > 0.0f) return 0; + + int mini = 0; + cpFloat poly_min = segValueOnAxis(seg, axes.n, axes.d); + if(poly_min > 0.0f) return 0; + for(int i=0; i<poly.numVerts; i++){ + cpFloat dist = segValueOnAxis(seg, axes[i].n, axes[i].d); + if(dist > 0.0f){ + return 0; + } else if(dist > poly_min){ + poly_min = dist; + mini = i; + } + } + + int num = 0; + + cpVect poly_n = cpvneg(axes[mini].n); + + cpVect va = cpvadd(seg.ta, cpvmult(poly_n, seg.r)); + cpVect vb = cpvadd(seg.tb, cpvmult(poly_n, seg.r)); + if(cpPolyShapeContainsVert(poly, va)) + cpContactInit(nextContactPoint(arr, &num), va, poly_n, poly_min, CP_HASH_PAIR(seg.shape.hashid, cast(cpHashValue)0)); + if(cpPolyShapeContainsVert(poly, vb)) + cpContactInit(nextContactPoint(arr, &num), vb, poly_n, poly_min, CP_HASH_PAIR(seg.shape.hashid, cast(cpHashValue)1)); + + // Floating point precision problems here. + // This will have to do for now. + poly_min -= cp_collision_slop; + if(minNorm >= poly_min || minNeg >= poly_min) { + if(minNorm > minNeg) + findPointsBehindSeg(arr, &num, seg, poly, minNorm, 1.0f); + else + findPointsBehindSeg(arr, &num, seg, poly, minNeg, -1.0f); + } + + // If no other collision points are found, try colliding endpoints. + if(num == 0){ + cpVect poly_a = poly.tVerts[mini]; + cpVect poly_b = poly.tVerts[(mini + 1)%poly.numVerts]; + + if(circle2circleQuery(seg.ta, poly_a, seg.r, 0.0f, arr)) + return 1; + + if(circle2circleQuery(seg.tb, poly_a, seg.r, 0.0f, arr)) + return 1; + + if(circle2circleQuery(seg.ta, poly_b, seg.r, 0.0f, arr)) + return 1; + + if(circle2circleQuery(seg.tb, poly_b, seg.r, 0.0f, arr)) + return 1; + } + + return num; +} + +// This one is less gross, but still gross. +// TODO: Comment me! +static int +circle2poly(const cpShape *shape1, const cpShape *shape2, cpContact *con) +{ + cpCircleShape *circ = cast(cpCircleShape *)shape1; + cpPolyShape *poly = cast(cpPolyShape *)shape2; + cpPolyShapeAxis *axes = poly.tAxes; + + int mini = 0; + cpFloat min = cpvdot(axes.n, circ.tc) - axes.d - circ.r; + for(int i=0; i<poly.numVerts; i++){ + cpFloat dist = cpvdot(axes[i].n, circ.tc) - axes[i].d - circ.r; + if(dist > 0.0f){ + return 0; + } else if(dist > min) { + min = dist; + mini = i; + } + } + + cpVect n = axes[mini].n; + cpVect a = poly.tVerts[mini]; + cpVect b = poly.tVerts[(mini + 1)%poly.numVerts]; + cpFloat dta = cpvcross(n, a); + cpFloat dtb = cpvcross(n, b); + cpFloat dt = cpvcross(n, circ.tc); + + if(dt < dtb){ + return circle2circleQuery(circ.tc, b, circ.r, 0.0f, con); + } else if(dt < dta) { + cpContactInit( + con, + cpvsub(circ.tc, cpvmult(n, circ.r + min/2.0f)), + cpvneg(n), + min, + 0 + ); + + return 1; + } else { + return circle2circleQuery(circ.tc, a, circ.r, 0.0f, con); + } +} + +static const collisionFunc builtinCollisionFuncs[9] = [ + &circle2circle, + null, + null, + &circle2segment, + null, + null, + &circle2poly, + &seg2poly, + &poly2poly, +]; + +static collisionFunc[cpShapeType.CP_NUM_SHAPES * cpShapeType.CP_NUM_SHAPES] colfuncs = builtinCollisionFuncs; + +static void +addColFunc(const cpShapeType a, const cpShapeType b, collisionFunc func) +{ + colfuncs[a + b*cpShapeType.CP_NUM_SHAPES] = func; +} + +// Initializes the array of collision functions. +// Called by cpInitChipmunk(). +void cpInitCollisionFuncs() +{ + addColFunc(cpShapeType.CP_CIRCLE_SHAPE, cpShapeType.CP_CIRCLE_SHAPE, &circle2circle); + addColFunc(cpShapeType.CP_CIRCLE_SHAPE, cpShapeType.CP_SEGMENT_SHAPE, &circle2segment); + addColFunc(cpShapeType.CP_SEGMENT_SHAPE, cpShapeType.CP_POLY_SHAPE, &seg2poly); + addColFunc(cpShapeType.CP_CIRCLE_SHAPE, cpShapeType.CP_POLY_SHAPE, &circle2poly); + addColFunc(cpShapeType.CP_POLY_SHAPE, cpShapeType.CP_POLY_SHAPE, &poly2poly); +} + + +int +cpCollideShapes(const cpShape *a, const cpShape *b, cpContact *arr) +{ + // Their shape types must be in order. + assert(a.klass.type <= b.klass.type, "Collision shapes passed to cpCollideShapes() are not sorted."); + + collisionFunc cfunc = colfuncs[a.klass.type + b.klass.type*cpShapeType.CP_NUM_SHAPES]; + return (cfunc) ? cfunc(a, b, arr) : 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpHashSet.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,277 @@ + +// written in the D programming language + +module chipmunkd.cpHashSet; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpArray; +import chipmunkd.prime; + +// cpHashSet uses a chained hashtable implementation. +// Other than the transformation functions, there is nothing fancy going on. + +// cpHashSetBin's form the linked lists in the chained hash table. +struct cpHashSetBin { + // Pointer to the element. + void *elt; + // Hash value of the element. + cpHashValue hash; + // Next element in the chain. + cpHashSetBin *next; +} + +// Equality function. Returns true if ptr is equal to elt. +alias cpBool function(void *ptr, void *elt) cpHashSetEqlFunc; +// Used by cpHashSetInsert(). Called to transform the ptr into an element. +alias void* function(void *ptr, void *data) cpHashSetTransFunc; + +struct cpHashSet { + // Number of elements stored in the table. + int entries; + // Number of cells in the table. + int size; + + cpHashSetEqlFunc eql; + cpHashSetTransFunc trans; + + // Default value returned by cpHashSetFind() when no element is found. + // Defaults to null. + void *default_value; + + // The table and recycled bins + cpHashSetBin **table; + cpHashSetBin *pooledBins; + + cpArray *allocatedBuffers; +} + +alias void function(void *elt, void *data) cpHashSetIterFunc; +alias cpBool function(void *elt, void *data) cpHashSetFilterFunc; + +static void freeWrap(void *ptr, void *unused){cpfree(ptr);} + +void +cpHashSetDestroy(cpHashSet *set) +{ + // Free the table. + cpfree(set.table); + + cpArrayEach(set.allocatedBuffers, &freeWrap, null); + cpArrayFree(set.allocatedBuffers); +} + +void +cpHashSetFree(cpHashSet *set) +{ + if(set){ + cpHashSetDestroy(set); + cpfree(set); + } +} + +cpHashSet * +cpHashSetAlloc() +{ + return cast(cpHashSet *)cpcalloc(1, cpHashSet.sizeof); +} + +cpHashSet * +cpHashSetInit(cpHashSet *set, int size, cpHashSetEqlFunc eqlFunc, cpHashSetTransFunc trans) +{ + set.size = next_prime(size); + set.entries = 0; + + set.eql = eqlFunc; + set.trans = trans; + + set.default_value = null; + + set.table = cast(cpHashSetBin **)cpcalloc(set.size, (cpHashSetBin *).sizeof); + set.pooledBins = null; + + set.allocatedBuffers = cpArrayNew(0); + + return set; +} + +cpHashSet * +cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc, cpHashSetTransFunc trans) +{ + return cpHashSetInit(cpHashSetAlloc(), size, eqlFunc, trans); +} + +static int +setIsFull(cpHashSet *set) +{ + return (set.entries >= set.size); +} + +static void +cpHashSetResize(cpHashSet *set) +{ + // Get the next approximate doubled prime. + int newSize = next_prime(set.size + 1); + // Allocate a new table. + cpHashSetBin **newTable = cast(cpHashSetBin **)cpcalloc(newSize, (cpHashSetBin *).sizeof); + + // Iterate over the chains. + for(int i=0; i<set.size; i++){ + // Rehash the bins into the new table. + cpHashSetBin *bin = set.table[i]; + while(bin){ + cpHashSetBin *next = bin.next; + + int idx = bin.hash%newSize; + bin.next = newTable[idx]; + newTable[idx] = bin; + + bin = next; + } + } + + cpfree(set.table); + + set.table = newTable; + set.size = newSize; +} + +static void +recycleBin(cpHashSet *set, cpHashSetBin *bin) +{ + bin.next = set.pooledBins; + set.pooledBins = bin; + bin.elt = null; +} + +static cpHashSetBin * +getUnusedBin(cpHashSet *set) +{ + cpHashSetBin *bin = set.pooledBins; + + if(bin){ + set.pooledBins = bin.next; + return bin; + } else { + // Pool is exhausted, make more + int count = CP_BUFFER_BYTES/cpHashSetBin.sizeof; + assert(count!=0, "Buffer size is too small."); + + cpHashSetBin *buffer = cast(cpHashSetBin *)cpmalloc(CP_BUFFER_BYTES); + cpArrayPush(set.allocatedBuffers, buffer); + + // push all but the first one, return the first instead + for(int i=1; i<count; i++) recycleBin(set, buffer + i); + return buffer; + } +} + +void * +cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, void *data) +{ + int idx = hash%set.size; + + // Find the bin with the matching element. + cpHashSetBin *bin = set.table[idx]; + while(bin && !set.eql(ptr, bin.elt)) + bin = bin.next; + + // Create it necessary. + if(!bin){ + bin = getUnusedBin(set); + bin.hash = hash; + bin.elt = set.trans(ptr, data); // Transform the pointer. + + bin.next = set.table[idx]; + set.table[idx] = bin; + + set.entries++; + + // Resize the set if it's full. + if(setIsFull(set)) + cpHashSetResize(set); + } + + return bin.elt; +} + +void * +cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr) +{ + int idx = hash%set.size; + + // Pointer to the previous bin pointer. + cpHashSetBin **prev_ptr = &set.table[idx]; + // Pointer the the current bin. + cpHashSetBin *bin = set.table[idx]; + + // Find the bin + while(bin && !set.eql(ptr, bin.elt)){ + prev_ptr = &bin.next; + bin = bin.next; + } + + // Remove it if it exists. + if(bin){ + // Update the previous bin pointer to point to the next bin. + (*prev_ptr) = bin.next; + set.entries--; + + void *return_value = bin.elt; + + recycleBin(set, bin); + + return return_value; + } + + return null; +} + +void * +cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr) +{ + int idx = hash%set.size; + cpHashSetBin *bin = set.table[idx]; + while(bin && !set.eql(ptr, bin.elt)) + bin = bin.next; + + return (bin ? bin.elt : set.default_value); +} + +void +cpHashSetEach(cpHashSet *set, cpHashSetIterFunc func, void *data) +{ + for(int i=0; i<set.size; i++){ + cpHashSetBin *bin = set.table[i]; + while(bin){ + cpHashSetBin *next = bin.next; + func(bin.elt, data); + bin = next; + } + } +} + +void +cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data) +{ + // Iterate over all the chains. + for(int i=0; i<set.size; i++){ + // The rest works similarly to cpHashSetRemove() above. + cpHashSetBin **prev_ptr = &set.table[i]; + cpHashSetBin *bin = set.table[i]; + while(bin){ + cpHashSetBin *next = bin.next; + + if(func(bin.elt, data)){ + prev_ptr = &bin.next; + } else { + (*prev_ptr) = next; + + set.entries--; + recycleBin(set, bin); + } + + bin = next; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpPolyShape.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,315 @@ + +// written in the D programming language + +module chipmunkd.cpPolyShape; + +import chipmunkd.cpShape; +import chipmunkd.cpBody; +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect_h; +import chipmunkd.cpBB; + +// Axis structure used by cpPolyShape. +struct cpPolyShapeAxis{ + // normal + cpVect n; + // distance from origin + cpFloat d; +} + +// Convex polygon shape structure. +struct cpPolyShape{ + cpShape shape; + + // Vertex and axis lists. + int numVerts; + cpVect *verts; + cpPolyShapeAxis *axes; + + // Transformed vertex and axis lists. + cpVect *tVerts; + cpPolyShapeAxis *tAxes; +} + +//// Basic allocation functions. +//cpPolyShape *cpPolyShapeAlloc(void); +//cpPolyShape *cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int numVerts, cpVect *verts, cpVect offset); +//cpShape *cpPolyShapeNew(cpBody *body, int numVerts, cpVect *verts, cpVect offset); +// +//cpPolyShape *cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height); +//cpShape *cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height); +// +//// Check that a set of vertexes has a correct winding and that they are convex +//cpBool cpPolyValidate(cpVect *verts, int numVerts); +// +//int cpPolyShapeGetNumVerts(cpShape *shape); +//cpVect cpPolyShapeGetVert(cpShape *shape, int idx); + +// *** inlined utility functions + +// Returns the minimum distance of the polygon to the axis. +static cpFloat +cpPolyShapeValueOnAxis(const cpPolyShape *poly, const cpVect n, const cpFloat d) +{ + const cpVect *verts = poly.tVerts; + cpFloat min = cpvdot(n, verts[0]); + + int i; + for(i=1; i<poly.numVerts; i++) + min = cpfmin(min, cpvdot(n, verts[i])); + + return min - d; +} + +// Returns true if the polygon contains the vertex. +static cpBool +cpPolyShapeContainsVert(const cpPolyShape *poly, const cpVect v) +{ + const cpPolyShapeAxis *axes = poly.tAxes; + + int i; + for(i=0; i<poly.numVerts; i++){ + cpFloat dist = cpvdot(axes[i].n, v) - axes[i].d; + if(dist > 0.0f) return cpFalse; + } + + return cpTrue; +} + +// Same as cpPolyShapeContainsVert() but ignores faces pointing away from the normal. +static cpBool +cpPolyShapeContainsVertPartial(const cpPolyShape *poly, const cpVect v, const cpVect n) +{ + const cpPolyShapeAxis *axes = poly.tAxes; + + int i; + for(i=0; i<poly.numVerts; i++){ + if(cpvdot(axes[i].n, n) < 0.0f) continue; + cpFloat dist = cpvdot(axes[i].n, v) - axes[i].d; + if(dist > 0.0f) return cpFalse; + } + + return cpTrue; +} + +// cpPolyShape.c --------------------------------- + + +cpPolyShape * +cpPolyShapeAlloc() +{ + return cast(cpPolyShape *)cpcalloc(1, cpPolyShape.sizeof); +} + +static void +cpPolyShapeTransformVerts(cpPolyShape *poly, cpVect p, cpVect rot) +{ + cpVect *src = poly.verts; + cpVect *dst = poly.tVerts; + + for(int i=0; i<poly.numVerts; i++) + dst[i] = cpvadd(p, cpvrotate(src[i], rot)); +} + +static void +cpPolyShapeTransformAxes(cpPolyShape *poly, cpVect p, cpVect rot) +{ + cpPolyShapeAxis *src = poly.axes; + cpPolyShapeAxis *dst = poly.tAxes; + + for(int i=0; i<poly.numVerts; i++){ + cpVect n = cpvrotate(src[i].n, rot); + dst[i].n = n; + dst[i].d = cpvdot(p, n) + src[i].d; + } +} + +static cpBB +cpPolyShapeCacheData(cpShape *shape, cpVect p, cpVect rot) +{ + cpPolyShape *poly = cast(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 void +cpPolyShapeDestroy(cpShape *shape) +{ + cpPolyShape *poly = cast(cpPolyShape *)shape; + + cpfree(poly.verts); + cpfree(poly.tVerts); + + cpfree(poly.axes); + cpfree(poly.tAxes); +} + +static cpBool +cpPolyShapePointQuery(cpShape *shape, cpVect p){ + return cpBBcontainsVect(shape.bb, p) && cpPolyShapeContainsVert(cast(cpPolyShape *)shape, p); +} + +static void +cpPolyShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info) +{ + cpPolyShape *poly = cast(cpPolyShape *)shape; + cpPolyShapeAxis *axes = poly.tAxes; + cpVect *verts = poly.tVerts; + int numVerts = poly.numVerts; + + for(int i=0; i<numVerts; i++){ + cpVect n = axes[i].n; + cpFloat an = cpvdot(a, n); + if(axes[i].d > an) continue; + + cpFloat bn = cpvdot(b, n); + cpFloat t = (axes[i].d - an)/(bn - an); + if(t < 0.0f || 1.0f < t) continue; + + cpVect point = cpvlerp(a, b, t); + cpFloat dt = -cpvcross(n, point); + cpFloat dtMin = -cpvcross(n, verts[i]); + cpFloat dtMax = -cpvcross(n, verts[(i+1)%numVerts]); + + if(dtMin <= dt && dt <= dtMax){ + info.shape = shape; + info.t = t; + info.n = n; + } + } +} + +static /+const+/ cpShapeClass polyClass = { + cpShapeType.CP_POLY_SHAPE, + &cpPolyShapeCacheData, + &cpPolyShapeDestroy, + &cpPolyShapePointQuery, + &cpPolyShapeSegmentQuery, +}; + +cpBool +cpPolyValidate(cpVect *verts, int numVerts) +{ + for(int i=0; i<numVerts; i++){ + cpVect a = verts[i]; + cpVect b = verts[(i+1)%numVerts]; + cpVect c = verts[(i+2)%numVerts]; + + if(cpvcross(cpvsub(b, a), cpvsub(c, b)) > 0.0f) + return cpFalse; + } + + return cpTrue; +} + +int +cpPolyShapeGetNumVerts(cpShape *shape) +{ + assert(shape.klass == &polyClass, "Shape is not a poly shape."); + return (cast(cpPolyShape *)shape).numVerts; +} + +cpVect +cpPolyShapeGetVert(cpShape *shape, int idx) +{ + assert(shape.klass == &polyClass, "Shape is not a poly shape."); + assert(0 <= idx && idx < cpPolyShapeGetNumVerts(shape), "Index out of range."); + + return (cast(cpPolyShape *)shape).verts[idx]; +} + + +static void +setUpVerts(cpPolyShape *poly, int numVerts, cpVect *verts, cpVect offset) +{ + poly.numVerts = numVerts; + + poly.verts = cast(cpVect *)cpcalloc(numVerts, cpVect.sizeof); + poly.tVerts = cast(cpVect *)cpcalloc(numVerts, cpVect.sizeof); + poly.axes = cast(cpPolyShapeAxis *)cpcalloc(numVerts, (cpPolyShapeAxis).sizeof); + poly.tAxes = cast(cpPolyShapeAxis *)cpcalloc(numVerts, (cpPolyShapeAxis).sizeof); + + for(int i=0; i<numVerts; i++){ + cpVect a = cpvadd(offset, verts[i]); + cpVect b = cpvadd(offset, verts[(i+1)%numVerts]); + cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); + + poly.verts[i] = a; + poly.axes[i].n = n; + poly.axes[i].d = cpvdot(n, a); + } +} + +cpPolyShape * +cpPolyShapeInit(cpPolyShape *poly, cpBody *_body, int numVerts, cpVect *verts, cpVect offset) +{ + // Fail if the user attempts to pass a concave poly, or a bad winding. + assert(cpPolyValidate(verts, numVerts), "Polygon is concave or has a reversed winding."); + + setUpVerts(poly, numVerts, verts, offset); + cpShapeInit(cast(cpShape *)poly, &polyClass, _body); + + return poly; +} + +cpShape * +cpPolyShapeNew(cpBody *_body, int numVerts, cpVect *verts, cpVect offset) +{ + return cast(cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), _body, numVerts, verts, offset); +} + +cpPolyShape * +cpBoxShapeInit(cpPolyShape *poly, cpBody *_body, cpFloat width, cpFloat height) +{ + cpFloat hw = width/2.0f; + cpFloat hh = height/2.0f; + + cpVect verts[] = [ + cpv(-hw,-hh), + cpv(-hw, hh), + cpv( hw, hh), + cpv( hw,-hh), + ]; + + return cpPolyShapeInit(poly, _body, 4, verts.ptr, cpvzero); +} + +cpShape * +cpBoxShapeNew(cpBody *_body, cpFloat width, cpFloat height) +{ + return cast(cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), _body, width, height); +} + +// Unsafe API (chipmunk_unsafe.h) + +void +cpPolyShapeSetVerts(cpShape *shape, int numVerts, cpVect *verts, cpVect offset) +{ + assert(shape.klass == &polyClass, "Shape is not a poly shape."); + cpPolyShapeDestroy(shape); + setUpVerts(cast(cpPolyShape *)shape, numVerts, verts, offset); +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpShape.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,533 @@ + +// written in the D programming language + +module chipmunkd.cpShape; + +import chipmunkd.cpBody; +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect_h; +import chipmunkd.cpBB; + +import std.stdio; + +struct cpSegmentQueryInfo { + cpShape *shape; // shape that was hit, null if no collision + cpFloat t; // Distance along query segment, will always be in the range [0, 1]. + cpVect n; // normal of hit surface +} + +// Enumeration of shape types. +enum cpShapeType{ + CP_CIRCLE_SHAPE, + CP_SEGMENT_SHAPE, + CP_POLY_SHAPE, + CP_NUM_SHAPES +} + +// Shape class. Holds function pointers and type data. +struct cpShapeClass { + cpShapeType type; + + // Called by cpShapeCacheBB(). + cpBB function(cpShape *shape, cpVect p, cpVect rot) cacheData; + // Called to by cpShapeDestroy(). + void function(cpShape *shape) destroy; + + // called by cpShapePointQuery(). + cpBool function(cpShape *shape, cpVect p)pointQuery; + + // called by cpShapeSegmentQuery() + void function(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info) segmentQuery; +} + +// Basic shape struct that the others inherit from. +struct cpShape{ + // The "class" of a shape as defined above + /+const+/ cpShapeClass *klass; + + // cpBody that the shape is attached to. + cpBody* _body; + + // Cached BBox for the shape. + cpBB bb; + + // Sensors invoke callbacks, but do not generate collisions + cpBool sensor; + + // *** Surface properties. + + // Coefficient of restitution. (elasticity) + cpFloat e; + // Coefficient of friction. + cpFloat u; + // Surface velocity used when solving for friction. + cpVect surface_v; + + // *** User Definable Fields + + // User defined data pointer for the shape. + cpDataPointer data; + + // User defined collision type for the shape. + cpCollisionType collision_type; + // User defined collision group for the shape. + cpGroup group; + // User defined layer bitmask for the shape. + cpLayers layers; + + // *** Internally Used Fields + + // Shapes form a linked list when added to space on a non-null body + cpShape* next; + + // Unique id used as the hash value. + cpHashValue hashid; +} + +// +//// Low level shape initialization func. +//cpShape* cpShapeInit(cpShape *shape, const struct cpShapeClass *klass, cpBody *body); +// +//// Basic destructor functions. (allocation functions are not shared) +//void cpShapeDestroy(cpShape *shape); +//void cpShapeFree(cpShape *shape); +// +//// Cache the BBox of the shape. +//cpBB cpShapeCacheBB(cpShape *shape); +// +//// Test if a point lies within a shape. +//cpBool cpShapePointQuery(cpShape *shape, cpVect p); +// +//#define CP_DeclareShapeGetter(struct, type, name) type struct##Get##name(cpShape *shape) +// + +// Circle shape structure. +struct cpCircleShape{ + cpShape shape; + + // Center in body space coordinates + cpVect c; + // Radius. + cpFloat r; + + // Transformed center. (world space coordinates) + cpVect tc; +} + +//// Basic allocation functions for cpCircleShape. +//cpCircleShape *cpCircleShapeAlloc(void); +//cpCircleShape *cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset); +//cpShape *cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset); +// +//CP_DeclareShapeGetter(cpCircleShape, cpVect, Offset); +//CP_DeclareShapeGetter(cpCircleShape, cpFloat, Radius); + +// Segment shape structure. +struct cpSegmentShape{ + cpShape shape; + + // Endpoints and normal of the segment. (body space coordinates) + cpVect a, b, n; + // Radius of the segment. (Thickness) + cpFloat r; + + // Transformed endpoints and normal. (world space coordinates) + cpVect ta, tb, tn; +} +// +//// Basic allocation functions for cpSegmentShape. +//cpSegmentShape* cpSegmentShapeAlloc(void); +//cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius); +//cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius); +// +//CP_DeclareShapeGetter(cpSegmentShape, cpVect, A); +//CP_DeclareShapeGetter(cpSegmentShape, cpVect, B); +//CP_DeclareShapeGetter(cpSegmentShape, cpVect, Normal); +//CP_DeclareShapeGetter(cpSegmentShape, cpFloat, Radius); +// +//// For determinism, you can reset the shape id counter. +//void cpResetShapeIdCounter(void); +// +//// Directed segment queries against individual shapes. +//void cpSegmentQueryInfoPrint(cpSegmentQueryInfo *info); +// +//cpBool cpShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info); +// +static cpVect +cpSegmentQueryHitPoint(const cpVect start, const cpVect end, const cpSegmentQueryInfo info) +{ + return cpvlerp(start, end, info.t); +} + +static cpFloat +cpSegmentQueryHitDist(const cpVect start, const cpVect end, const cpSegmentQueryInfo info) +{ + return cpvdist(start, end)*info.t; +} + +cpHashValue SHAPE_ID_COUNTER = 0; + +void +cpResetShapeIdCounter() +{ + SHAPE_ID_COUNTER = 0; +} + +cpShape* +cpShapeInit(cpShape *shape, /+const+/ cpShapeClass *klass, cpBody *_body) +{ + shape.klass = klass; + + shape.hashid = SHAPE_ID_COUNTER; + SHAPE_ID_COUNTER++; + + shape._body = _body; + shape.sensor = 0; + + shape.e = 0.0f; + shape.u = 0.0f; + shape.surface_v = cpvzero; + + shape.collision_type = 0; + shape.group = CP_NO_GROUP; + shape.layers = CP_ALL_LAYERS; + + shape.data = null; + shape.next = null; + +// cpShapeCacheBB(shape); + + return shape; +} + +void +cpShapeDestroy(cpShape *shape) +{ + if(shape.klass.destroy) shape.klass.destroy(shape); +} + +void +cpShapeFree(cpShape *shape) +{ + if(shape){ + cpShapeDestroy(shape); + cpfree(shape); + } +} + +cpBB +cpShapeCacheBB(cpShape *shape) +{ + cpBody *_body = shape._body; + + shape.bb = shape.klass.cacheData(shape, _body.p, _body.rot); + return shape.bb; +} + +cpBool +cpShapePointQuery(cpShape *shape, cpVect p){ + return shape.klass.pointQuery(shape, p); +} + +cpBool +cpShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info){ + cpSegmentQueryInfo blank = {null, 0.0f, cpvzero}; + (*info) = blank; + + shape.klass.segmentQuery(shape, a, b, info); + return (info.shape !is null); +} + +void +cpSegmentQueryInfoPrint(cpSegmentQueryInfo *info) +{ + writefln("Segment Query:\n"); + writefln("\tt: %s\n", info.t); +// writefln("\tdist: %f\n", info.dist); +// writefln("\tpoint: %s\n", cpvstr(info.point)); + //writefln("\tn: %s\n", cpvstr(info.n)); //TODO: +} + +cpCircleShape * +cpCircleShapeAlloc() +{ + return cast(cpCircleShape *)cpcalloc(1, cpCircleShape.sizeof); +} + +static cpBB +bbFromCircle(const cpVect c, const cpFloat r) +{ + return cpBBNew(c.x-r, c.y-r, c.x+r, c.y+r); +} + +static cpBB +cpCircleShapeCacheData(cpShape *shape, cpVect p, cpVect rot) +{ + cpCircleShape *circle = cast(cpCircleShape *)shape; + + circle.tc = cpvadd(p, cpvrotate(circle.c, rot)); + return bbFromCircle(circle.tc, circle.r); +} + +static cpBool +cpCircleShapePointQuery(cpShape *shape, cpVect p){ + cpCircleShape *circle = cast(cpCircleShape *)shape; + return cpvnear(circle.tc, p, circle.r); +} + +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); + + 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 det = qb*qb - 4.0f*qa*qc; + + if(det >= 0.0f){ + cpFloat t = (-qb - cpfsqrt(det))/(2.0f*qa); + if(0.0f<= t && t <= 1.0f){ + info.shape = shape; + info.t = t; + info.n = cpvnormalize(cpvlerp(a, b, t)); + } + } +} + +static void +cpCircleShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info) +{ + cpCircleShape *circle = cast(cpCircleShape *)shape; + circleSegmentQuery(shape, circle.tc, circle.r, a, b, info); +} + +static /+const+/ cpShapeClass cpCircleShapeClass = { + cpShapeType.CP_CIRCLE_SHAPE, + &cpCircleShapeCacheData, + null, + &cpCircleShapePointQuery, + &cpCircleShapeSegmentQuery, +}; + +cpCircleShape * +cpCircleShapeInit(cpCircleShape *circle, cpBody *_body, cpFloat radius, cpVect offset) +{ + circle.c = offset; + circle.r = radius; + + cpShapeInit(cast(cpShape *)circle, &cpCircleShapeClass, _body); + + return circle; +} + +cpShape * +cpCircleShapeNew(cpBody *_body, cpFloat radius, cpVect offset) +{ + return cast(cpShape *)cpCircleShapeInit(cpCircleShapeAlloc(), _body, radius, offset); +} +//TODO: +//CP_DefineShapeGetter(cpCircleShape, cpVect, c, Offset) +//CP_DefineShapeGetter(cpCircleShape, cpFloat, r, Radius) + +cpSegmentShape * +cpSegmentShapeAlloc() +{ + return cast(cpSegmentShape *)cpcalloc(1, cpSegmentShape.sizeof); +} + +static cpBB +cpSegmentShapeCacheData(cpShape *shape, cpVect p, cpVect rot) +{ + cpSegmentShape *seg = cast(cpSegmentShape *)shape; + + seg.ta = cpvadd(p, cpvrotate(seg.a, rot)); + seg.tb = cpvadd(p, cpvrotate(seg.b, rot)); + seg.tn = cpvrotate(seg.n, rot); + + cpFloat l,r,s,t; + + if(seg.ta.x < seg.tb.x){ + l = seg.ta.x; + r = seg.tb.x; + } else { + l = seg.tb.x; + r = seg.ta.x; + } + + if(seg.ta.y < seg.tb.y){ + s = seg.ta.y; + t = seg.tb.y; + } else { + s = seg.tb.y; + t = seg.ta.y; + } + + cpFloat rad = seg.r; + return cpBBNew(l - rad, s - rad, r + rad, t + rad); +} + +static cpBool +cpSegmentShapePointQuery(cpShape *shape, cpVect p){ + if(!cpBBcontainsVect(shape.bb, p)) return cpFalse; + + cpSegmentShape *seg = cast(cpSegmentShape *)shape; + + // Calculate normal distance from segment. + cpFloat dn = cpvdot(seg.tn, p) - cpvdot(seg.ta, seg.tn); + cpFloat dist = cpfabs(dn) - seg.r; + if(dist > 0.0f) return cpFalse; + + // Calculate tangential distance along segment. + cpFloat dt = -cpvcross(seg.tn, p); + cpFloat dtMin = -cpvcross(seg.tn, seg.ta); + cpFloat dtMax = -cpvcross(seg.tn, seg.tb); + + // Decision tree to decide which feature of the segment to collide with. + if(dt <= dtMin){ + if(dt < (dtMin - seg.r)){ + return cpFalse; + } else { + return cpvlengthsq(cpvsub(seg.ta, p)) < (seg.r*seg.r); + } + } else { + if(dt < dtMax){ + return cpTrue; + } else { + if(dt < (dtMax + seg.r)) { + return cpvlengthsq(cpvsub(seg.tb, p)) < (seg.r*seg.r); + } else { + return cpFalse; + } + } + } + + return cpTrue; +} + +static cpBool inUnitRange(cpFloat t){return (0.0f < t && t < 1.0f);} + +static void +cpSegmentShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info) +{ + // TODO this function could be optimized better. + + cpSegmentShape *seg = cast(cpSegmentShape *)shape; + cpVect n = seg.tn; + // flip n if a is behind the axis + if(cpvdot(a, n) < cpvdot(seg.ta, n)) + n = cpvneg(n); + + cpFloat an = cpvdot(a, n); + cpFloat bn = cpvdot(b, n); + + if(an != bn){ + cpFloat d = cpvdot(seg.ta, n) + seg.r; + cpFloat t = (d - an)/(bn - an); + + if(0.0f < t && t < 1.0f){ + cpVect point = cpvlerp(a, b, t); + cpFloat dt = -cpvcross(seg.tn, point); + cpFloat dtMin = -cpvcross(seg.tn, seg.ta); + cpFloat dtMax = -cpvcross(seg.tn, seg.tb); + + if(dtMin < dt && dt < dtMax){ + info.shape = shape; + info.t = t; + info.n = n; + + return; // don't continue on and check endcaps + } + } + } + + if(seg.r) { + cpSegmentQueryInfo info1 = {null, 1.0f, cpvzero}; + cpSegmentQueryInfo info2 = {null, 1.0f, cpvzero}; + circleSegmentQuery(shape, seg.ta, seg.r, a, b, &info1); + circleSegmentQuery(shape, seg.tb, seg.r, a, b, &info2); + + if(info1.t < info2.t){ + (*info) = info1; + } else { + (*info) = info2; + } + } +} + +static /+const+/ cpShapeClass cpSegmentShapeClass = { + cpShapeType.CP_SEGMENT_SHAPE, + &cpSegmentShapeCacheData, + null, + &cpSegmentShapePointQuery, + &cpSegmentShapeSegmentQuery, +}; + +cpSegmentShape * +cpSegmentShapeInit(cpSegmentShape *seg, cpBody *_body, cpVect a, cpVect b, cpFloat r) +{ + seg.a = a; + seg.b = b; + seg.n = cpvperp(cpvnormalize(cpvsub(b, a))); + + seg.r = r; + + cpShapeInit(cast(cpShape *)seg, &cpSegmentShapeClass, _body); + + return seg; +} + +cpShape* +cpSegmentShapeNew(cpBody *_body, cpVect a, cpVect b, cpFloat r) +{ + return cast(cpShape *)cpSegmentShapeInit(cpSegmentShapeAlloc(), _body, a, b, r); +} +//TODO: +//CP_DefineShapeGetter(cpSegmentShape, cpVect, a, A) +//CP_DefineShapeGetter(cpSegmentShape, cpVect, b, B) +//CP_DefineShapeGetter(cpSegmentShape, cpVect, n, Normal) +//CP_DefineShapeGetter(cpSegmentShape, cpFloat, r, Radius) + +// Unsafe API (chipmunk_unsafe.h) + +void +cpCircleShapeSetRadius(cpShape *shape, cpFloat radius) +{ + assert(shape.klass is &cpCircleShapeClass, "Shape is not a circle shape."); + cpCircleShape *circle = cast(cpCircleShape *)shape; + + circle.r = radius; +} + +void +cpCircleShapeSetOffset(cpShape *shape, cpVect offset) +{ + assert(shape.klass is &cpCircleShapeClass, "Shape is not a circle shape."); + cpCircleShape *circle = cast(cpCircleShape *)shape; + + circle.c = offset; +} + +void +cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b) +{ + assert(shape.klass is &cpSegmentShapeClass, "Shape is not a segment shape."); + cpSegmentShape *seg = cast(cpSegmentShape *)shape; + + seg.a = a; + seg.b = b; + seg.n = cpvperp(cpvnormalize(cpvsub(b, a))); +} + +void +cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius) +{ + assert(shape.klass is &cpSegmentShapeClass, "Shape is not a segment shape."); + cpSegmentShape *seg = cast(cpSegmentShape *)shape; + + seg.r = radius; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpSpace.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,642 @@ + +// written in the D programming language + +module chipmunkd.cpSpace; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.cpHashSet; +import chipmunkd.cpCollision; +import chipmunkd.cpBody; +import chipmunkd.cpArray; +import chipmunkd.cpShape; +import chipmunkd.cpBB; +import chipmunkd.cpArbiter; +import chipmunkd.cpSpaceHash; +import chipmunkd.constraints.cpConstraint; +import chipmunkd.cpSpaceQuery; + +// Number of frames that contact information should persist. +//extern cpTimestamp cp_contact_persistence; + +// User collision handler function types. +alias cpBool function(cpArbiter *arb, cpSpace *space, void *data)cpCollisionBeginFunc; +alias cpBool function(cpArbiter *arb, cpSpace *space, void *data)cpCollisionPreSolveFunc; +alias void function(cpArbiter *arb, cpSpace *space, void *data)cpCollisionPostSolveFunc; +alias void function(cpArbiter *arb, cpSpace *space, void *data)cpCollisionSeparateFunc; + +// Structure for holding collision pair function information. +// Used internally. +struct cpCollisionHandler { + cpCollisionType a; + cpCollisionType b; + cpCollisionBeginFunc begin; + cpCollisionPreSolveFunc preSolve; + cpCollisionPostSolveFunc postSolve; + cpCollisionSeparateFunc separate; + void *data; +} + +enum CP_MAX_CONTACTS_PER_ARBITER = 6; +struct cpContactBufferHeader { + cpTimestamp stamp; + cpContactBufferHeader *next; + uint numContacts; +} + +struct cpSpace{ + // *** User definable fields + + // Number of iterations to use in the impulse solver to solve contacts. + int iterations; + + // Number of iterations to use in the impulse solver to solve elastic collisions. + int elasticIterations; + + // Default gravity to supply when integrating rigid body motions. + cpVect gravity; + + // Default damping to supply when integrating rigid body motions. + cpFloat damping; + + // Speed threshold for a body to be considered idle. + // The default value of 0 means to let the space guess a good threshold based on gravity. + cpFloat idleSpeedThreshold; + + // Time a group of bodies must remain idle in order to fall asleep + // The default value of INFINITY disables the sleeping algorithm. + cpFloat sleepTimeThreshold; + + // *** Internally Used Fields + + // When the space is locked, you should not add or remove objects; + cpBool locked; + + // Time stamp. Is incremented on every call to cpSpaceStep(). + cpTimestamp stamp; + + // The static and active shape spatial hashes. + cpSpaceHash* staticShapes; + cpSpaceHash* activeShapes; + + // List of bodies in the system. + cpArray *bodies; + + // List of groups of sleeping bodies. + cpArray *sleepingComponents; + + // List of active arbiters for the impulse solver. + cpArray* arbiters, pooledArbiters; + + // Linked list ring of contact buffers. + // Head is the newest buffer, and each buffer points to a newer buffer. + // Head wraps around and points to the oldest (tail) buffer. + cpContactBufferHeader* contactBuffersHead, _contactBuffersTail; + + // List of buffers to be free()ed when destroying the space. + cpArray *allocatedBuffers; + + // Persistant contact set. + cpHashSet *contactSet; + + // List of constraints in the system. + cpArray *constraints; + + // Set of collisionpair functions. + cpHashSet *collFuncSet; + // Default collision handler. + cpCollisionHandler defaultHandler; + + cpHashSet *postStepCallbacks; + + cpBody staticBody; +} + +//// Basic allocation/destruction functions. +//cpSpace* cpSpaceAlloc(void); +//cpSpace* cpSpaceInit(cpSpace *space); +//cpSpace* cpSpaceNew(void); +// +//void cpSpaceDestroy(cpSpace *space); +//void cpSpaceFree(cpSpace *space); +// +//// Convenience function. Frees all referenced entities. (bodies, shapes and constraints) +//void cpSpaceFreeChildren(cpSpace *space); +// +//// Collision handler management functions. +//void cpSpaceSetDefaultCollisionHandler( +// cpSpace *space, +// cpCollisionBeginFunc begin, +// cpCollisionPreSolveFunc preSolve, +// cpCollisionPostSolveFunc postSolve, +// cpCollisionSeparateFunc separate, +// void *data +//); +//void cpSpaceAddCollisionHandler( +// cpSpace *space, +// cpCollisionType a, cpCollisionType b, +// cpCollisionBeginFunc begin, +// cpCollisionPreSolveFunc preSolve, +// cpCollisionPostSolveFunc postSolve, +// cpCollisionSeparateFunc separate, +// void *data +//); +//void cpSpaceRemoveCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b); +// +//// Add and remove entities from the system. +//cpShape *cpSpaceAddShape(cpSpace *space, cpShape *shape); +//cpShape *cpSpaceAddStaticShape(cpSpace *space, cpShape *shape); +//cpBody *cpSpaceAddBody(cpSpace *space, cpBody *body); +//cpConstraint *cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint); +// +//void cpSpaceRemoveShape(cpSpace *space, cpShape *shape); +//void cpSpaceRemoveStaticShape(cpSpace *space, cpShape *shape); +//void cpSpaceRemoveBody(cpSpace *space, cpBody *body); +//void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint); +// +//// Post Step function definition +alias void function(cpSpace *space, void *obj, void *data) cpPostStepFunc; +//// Register a post step function to be called after cpSpaceStep() has finished. +//// obj is used a key, you can only register one callback per unique value for obj +//void cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *obj, void *data); +// +//// Point query callback function +alias void function(cpShape *shape, void *data) cpSpacePointQueryFunc; +//void cpSpacePointQuery(cpSpace *space, cpVect point, cpLayers layers, cpGroup group, cpSpacePointQueryFunc func, void *data); +//cpShape *cpSpacePointQueryFirst(cpSpace *space, cpVect point, cpLayers layers, cpGroup group); +// +//// Segment query callback function +alias void function(cpShape *shape, cpFloat t, cpVect n, void *data)cpSpaceSegmentQueryFunc; +//void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpLayers layers, cpGroup group, cpSpaceSegmentQueryFunc func, void *data); +//cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpLayers layers, cpGroup group, cpSegmentQueryInfo *out); +// +//// BB query callback function +alias void function(cpShape *shape, void *data)cpSpaceBBQueryFunc; +//void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpLayers layers, cpGroup group, cpSpaceBBQueryFunc func, void *data); +// +// +//// Iterator function for iterating the bodies in a space. +alias void function(cpBody *_body, void *data)cpSpaceBodyIterator; +//void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIterator func, void *data); +// +//// Spatial hash management functions. +//void cpSpaceResizeStaticHash(cpSpace *space, cpFloat dim, int count); +//void cpSpaceResizeActiveHash(cpSpace *space, cpFloat dim, int count); +//void cpSpaceRehashStatic(cpSpace *space); +// +//void cpSpaceRehashShape(cpSpace *space, cpShape *shape); +// +//// Update the space. +//void cpSpaceStep(cpSpace *space, cpFloat dt); + + +cpTimestamp cp_contact_persistence = 3; + +// Equal function for contactSet. +static cpBool +contactSetEql(cpShape **shapes, cpArbiter *arb) +{ + cpShape *a = shapes[0]; + cpShape *b = shapes[1]; + + return ((a == arb.private_a && b == arb.private_b) || (b == arb.private_a && a == arb.private_b)); +} + +// Transformation function for contactSet. +static void * +contactSetTrans(cpShape **shapes, cpSpace *space) +{ + if(space.pooledArbiters.num == 0){ + // arbiter pool is exhausted, make more + int count = CP_BUFFER_BYTES/cpArbiter.sizeof; + assert(count, "Buffer size too small."); + + cpArbiter *buffer = cast(cpArbiter *)cpmalloc(CP_BUFFER_BYTES); + cpArrayPush(space.allocatedBuffers, buffer); + + for(int i=0; i<count; i++) cpArrayPush(space.pooledArbiters, buffer + i); + } + + return cpArbiterInit(cast(cpArbiter *) cpArrayPop(space.pooledArbiters), shapes[0], shapes[1]); +} + +// Equals function for collFuncSet. +static cpBool +collFuncSetEql(cpCollisionHandler *check, cpCollisionHandler *pair) +{ + return ((check.a == pair.a && check.b == pair.b) || (check.b == pair.a && check.a == pair.b)); +} + +// Transformation function for collFuncSet. +static void * +collFuncSetTrans(cpCollisionHandler *handler, void *unused) +{ + cpCollisionHandler *copy = cast(cpCollisionHandler *)cpmalloc(cpCollisionHandler.sizeof); + (*copy) = (*handler); + + return copy; +} + +// Default collision functions. +static cpBool alwaysCollide(cpArbiter *arb, cpSpace *space, void *data){return 1;} +static void nothing(cpArbiter *arb, cpSpace *space, void *data){} + +// BBfunc callback for the spatial hash. +static cpBB shapeBBFunc(cpShape *shape){return shape.bb;} + +// Iterator functions for destructors. +static void freeWrap(void *ptr, void *unused){ cpfree(ptr);} +static void shapeFreeWrap(cpShape *ptr, void *unused){ cpShapeFree(ptr);} +static void bodyFreeWrap(cpBody *ptr, void *unused){ cpBodyFree(ptr);} +static void constraintFreeWrap(cpConstraint *ptr, void *unused){cpConstraintFree(ptr);} + +cpSpace * +cpSpaceAlloc() +{ + return cast(cpSpace *)cpcalloc(1, cpSpace.sizeof); +} + +enum DEFAULT_DIM_SIZE = 100.0f; +enum DEFAULT_COUNT = 1000; +enum DEFAULT_ITERATIONS = 10; +enum DEFAULT_ELASTIC_ITERATIONS = 0; + +cpCollisionHandler defaultHandler = {0, 0, &alwaysCollide, &alwaysCollide, ¬hing, ¬hing, null}; + +cpSpace* +cpSpaceInit(cpSpace *space) +{ + space.iterations = DEFAULT_ITERATIONS; + space.elasticIterations = DEFAULT_ELASTIC_ITERATIONS; +// space.sleepTicks = 300; + + space.gravity = cpvzero; + space.damping = 1.0f; + + space.locked = 0; + space.stamp = 0; + + space.staticShapes = cpSpaceHashNew(DEFAULT_DIM_SIZE, DEFAULT_COUNT, cast(cpSpaceHashBBFunc)&shapeBBFunc); + space.activeShapes = cpSpaceHashNew(DEFAULT_DIM_SIZE, DEFAULT_COUNT, cast(cpSpaceHashBBFunc)&shapeBBFunc); + + space.allocatedBuffers = cpArrayNew(0); + + space.bodies = cpArrayNew(0); + space.sleepingComponents = cpArrayNew(0); + space.sleepTimeThreshold = INFINITY; + space.idleSpeedThreshold = 0.0f; + + space.arbiters = cpArrayNew(0); + space.pooledArbiters = cpArrayNew(0); + + space.contactBuffersHead = null; + space.contactSet = cpHashSetNew(0, cast(cpHashSetEqlFunc)&contactSetEql, cast(cpHashSetTransFunc)&contactSetTrans); + + space.constraints = cpArrayNew(0); + + space.defaultHandler = defaultHandler; + space.collFuncSet = cpHashSetNew(0, cast(cpHashSetEqlFunc)&collFuncSetEql, cast(cpHashSetTransFunc)&collFuncSetTrans); + space.collFuncSet.default_value = &space.defaultHandler; + + space.postStepCallbacks = null; + + cpBodyInit(&space.staticBody, INFINITY, INFINITY); + space.staticBody.space = space; + + return space; +} + +cpSpace* +cpSpaceNew() +{ + return cpSpaceInit(cpSpaceAlloc()); +} + +void +cpSpaceDestroy(cpSpace *space) +{ + cpSpaceHashFree(space.staticShapes); + cpSpaceHashFree(space.activeShapes); + + cpArrayFree(space.bodies); + cpArrayFree(space.sleepingComponents); + + cpArrayFree(space.constraints); + + cpHashSetFree(space.contactSet); + + cpArrayFree(space.arbiters); + cpArrayFree(space.pooledArbiters); + + if(space.allocatedBuffers){ + cpArrayEach(space.allocatedBuffers, &freeWrap, null); + cpArrayFree(space.allocatedBuffers); + } + + if(space.postStepCallbacks){ + cpHashSetEach(space.postStepCallbacks, &freeWrap, null); + cpHashSetFree(space.postStepCallbacks); + } + + if(space.collFuncSet){ + cpHashSetEach(space.collFuncSet, &freeWrap, null); + cpHashSetFree(space.collFuncSet); + } +} + +void +cpSpaceFree(cpSpace *space) +{ + if(space){ + cpSpaceDestroy(space); + cpfree(space); + } +} + +void +cpSpaceFreeChildren(cpSpace *space) +{ + cpArray *components = space.sleepingComponents; + while(components.num) cpBodyActivate(cast(cpBody *)components.arr[0]); + + cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&shapeFreeWrap, null); + cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&shapeFreeWrap, null); + cpArrayEach(space.bodies, cast(cpArrayIter)&bodyFreeWrap, null); + cpArrayEach(space.constraints, cast(cpArrayIter)&constraintFreeWrap, null); +} + +void +cpSpaceAddCollisionHandler( + cpSpace *space, + cpCollisionType a, cpCollisionType b, + cpCollisionBeginFunc begin, + cpCollisionPreSolveFunc preSolve, + cpCollisionPostSolveFunc postSolve, + cpCollisionSeparateFunc separate, + void *data +){ + // Remove any old function so the new one will get added. + cpSpaceRemoveCollisionHandler(space, a, b); + + cpCollisionHandler handler = { + a, b, + begin ? begin : &alwaysCollide, + preSolve ? preSolve : &alwaysCollide, + postSolve ? postSolve : ¬hing, + separate ? separate : ¬hing, + data + }; + + cpHashSetInsert(space.collFuncSet, CP_HASH_PAIR(a, b), &handler, null); +} + +void +cpSpaceRemoveCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b) +{ + struct tmp{cpCollisionType a, b;} tmp ids = {a, b}; + cpCollisionHandler *old_handler = cast(cpCollisionHandler *) cpHashSetRemove(space.collFuncSet, CP_HASH_PAIR(a, b), &ids); + cpfree(old_handler); +} + +void +cpSpaceSetDefaultCollisionHandler( + cpSpace *space, + cpCollisionBeginFunc begin, + cpCollisionPreSolveFunc preSolve, + cpCollisionPostSolveFunc postSolve, + cpCollisionSeparateFunc separate, + void *data +){ + cpCollisionHandler handler = { + 0, 0, + begin ? begin : &alwaysCollide, + preSolve ? preSolve : &alwaysCollide, + postSolve ? postSolve : ¬hing, + separate ? separate : ¬hing, + data + }; + + space.defaultHandler = handler; +} + +//#pragma mark Body, Shape, and Joint Management + +void cpAssertSpaceUnlocked(cpSpace* _space){ + assert(!_space.locked, + "This addition/removal cannot be done safely during a call to cpSpaceStep(). " + "Put these calls into a Post Step Callback." + );} + +static void +cpBodyAddShape(cpBody *_body, cpShape *shape) +{ + shape.next = shape._body.shapesList; + shape._body.shapesList = shape; +} + +static void +cpBodyRemoveShape(cpBody *_body, cpShape *shape) +{ + cpShape **prev_ptr = &_body.shapesList; + cpShape *node = _body.shapesList; + + while(node && node != shape){ + prev_ptr = &node.next; + node = node.next; + } + + assert(node, "Attempted to remove a shape from a body it was never attached to."); + (*prev_ptr) = node.next; +} + +cpShape * +cpSpaceAddShape(cpSpace *space, cpShape *shape) +{ + cpBody *_body = shape._body; + if(!_body || _body == &space.staticBody) return cpSpaceAddStaticShape(space, shape); + + assert(!cpHashSetFind(space.activeShapes.handleSet, shape.hashid, shape), + "Cannot add the same shape more than once."); + cpAssertSpaceUnlocked(space); + + cpBodyActivate(_body); + cpBodyAddShape(_body, shape); + + cpShapeCacheBB(shape); + cpSpaceHashInsert(space.activeShapes, shape, shape.hashid, shape.bb); + + return shape; +} + +static void +activateShapesTouchingShapeHelper(cpShape *shape, void *unused) +{ + cpBodyActivate(shape._body); +} + +static void +activateShapesTouchingShape(cpSpace *space, cpShape *shape) +{ + // TODO this query should be more precise + // Use shape queries once they are written + cpSpaceBBQuery(space, shape.bb, shape.layers, shape.group, &activateShapesTouchingShapeHelper, null); +} + +cpShape * +cpSpaceAddStaticShape(cpSpace *space, cpShape *shape) +{ + assert(!cpHashSetFind(space.staticShapes.handleSet, shape.hashid, shape), + "Cannot add the same static shape more than once."); + cpAssertSpaceUnlocked(space); + + if(!shape._body) shape._body = &space.staticBody; + + cpShapeCacheBB(shape); + activateShapesTouchingShape(space, shape); + cpSpaceHashInsert(space.staticShapes, shape, shape.hashid, shape.bb); + + return shape; +} + +cpBody * +cpSpaceAddBody(cpSpace *space, cpBody *_body) +{ + cpAssertWarn(_body.m != INFINITY, "Did you really mean to add an infinite mass body to the space?"); + assert(!_body.space, "Cannot add a body to a more than one space or to the same space twice."); +// cpAssertSpaceUnlocked(space); This should be safe as long as it's not from an integration callback + + cpArrayPush(space.bodies, _body); + _body.space = space; + + return _body; +} + +cpConstraint * +cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint) +{ + assert(!cpArrayContains(space.constraints, constraint), "Cannot add the same constraint more than once."); +// cpAssertSpaceUnlocked(space); This should be safe as long as its not from a constraint callback. + + if(!constraint.a) constraint.a = &space.staticBody; + if(!constraint.b) constraint.b = &space.staticBody; + + cpBodyActivate(constraint.a); + cpBodyActivate(constraint.b); + cpArrayPush(space.constraints, constraint); + + return constraint; +} + +struct removalContext { + cpSpace *space; + cpShape *shape; +} + +// Hashset filter func to throw away old arbiters. +static cpBool +contactSetFilterRemovedShape(cpArbiter *arb, removalContext *context) +{ + if(context.shape == arb.private_a || context.shape == arb.private_b){ + arb.handler.separate(arb, context.space, arb.handler.data); + cpArrayPush(context.space.pooledArbiters, arb); + return cpFalse; + } + + return cpTrue; +} + +void +cpSpaceRemoveShape(cpSpace *space, cpShape *shape) +{ + cpBody *_body = shape._body; + if(cpBodyIsStatic(_body)){ + cpSpaceRemoveStaticShape(space, shape); + return; + } + + cpBodyActivate(_body); + + cpAssertSpaceUnlocked(space); + cpAssertWarn(cpHashSetFind(space.activeShapes.handleSet, shape.hashid, shape) !is null, + "Cannot remove a shape that was never added to the space. (Removed twice maybe?)"); + + cpBodyRemoveShape(_body, shape); + + removalContext context = {space, shape}; + cpHashSetFilter(space.contactSet, cast(cpHashSetFilterFunc)&contactSetFilterRemovedShape, &context); + cpSpaceHashRemove(space.activeShapes, shape, shape.hashid); +} + +void +cpSpaceRemoveStaticShape(cpSpace *space, cpShape *shape) +{ + cpAssertWarn(cpHashSetFind(space.staticShapes.handleSet, shape.hashid, shape) !is null, + "Cannot remove a static or sleeping shape that was never added to the space. (Removed twice maybe?)"); + cpAssertSpaceUnlocked(space); + + removalContext context = {space, shape}; + cpHashSetFilter(space.contactSet, cast(cpHashSetFilterFunc)&contactSetFilterRemovedShape, &context); + cpSpaceHashRemove(space.staticShapes, shape, shape.hashid); + + activateShapesTouchingShape(space, shape); +} + +void +cpSpaceRemoveBody(cpSpace *space, cpBody *_body) +{ + cpAssertWarn(_body.space == space, + "Cannot remove a body that was never added to the space. (Removed twice maybe?)"); + cpAssertSpaceUnlocked(space); + + cpBodyActivate(_body); + cpArrayDeleteObj(space.bodies, _body); + _body.space = null; +} + +void +cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint) +{ + cpAssertWarn(cpArrayContains(space.constraints, constraint), + "Cannot remove a constraint that was never added to the space. (Removed twice maybe?)"); +// cpAssertSpaceUnlocked(space); Should be safe as long as its not from a constraint callback. + + cpBodyActivate(constraint.a); + cpBodyActivate(constraint.b); + cpArrayDeleteObj(space.constraints, constraint); +} + +//#pragma mark Spatial Hash Management + +static void updateBBCache(cpShape *shape, void *unused){cpShapeCacheBB(shape);} + +void +cpSpaceResizeStaticHash(cpSpace *space, cpFloat dim, int count) +{ + cpSpaceHashResize(space.staticShapes, dim, count); + cpSpaceHashRehash(space.staticShapes); +} + +void +cpSpaceResizeActiveHash(cpSpace *space, cpFloat dim, int count) +{ + cpSpaceHashResize(space.activeShapes, dim, count); +} + +void +cpSpaceRehashStatic(cpSpace *space) +{ + cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&updateBBCache, null); + cpSpaceHashRehash(space.staticShapes); +} + +void +cpSpaceRehashShape(cpSpace *space, cpShape *shape) +{ + cpShapeCacheBB(shape); + + // attempt to rehash the shape in both hashes + cpSpaceHashRehashObject(space.activeShapes, shape, shape.hashid); + cpSpaceHashRehashObject(space.staticShapes, shape, shape.hashid); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpSpaceComponent.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,224 @@ + +// written in the D programming language + +module chipmunkd.cpSpaceComponent; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.cpBody; + +// Chipmunk uses a data structure called a disjoint set forest. +// My attempts to find a way to splice circularly linked lists in +// constant time failed, and so I found this neat data structure instead. + +static cpBody * +componentNodeRoot(cpBody *_body) +{ + cpBody *parent = _body.node.parent; + + if(parent){ + // path compression, attaches this node directly to the root + return (_body.node.parent = componentNodeRoot(parent)); + } else { + return _body; + } +} + +static void +componentNodeMerge(cpBody *a_root, cpBody *b_root) +{ + if(a_root.node.rank < b_root.node.rank){ + a_root.node.parent = b_root; + } else if(a_root.node.rank > b_root.node.rank){ + b_root.node.parent = a_root; + } else if(a_root != b_root){ + b_root.node.parent = a_root; + a_root.node.rank++; + } +} + +static void +componentActivate(cpBody *root) +{ + if(!cpBodyIsSleeping(root)) return; + + cpSpace *space = root.space; + assert(space, "Trying to activate a body that was never added to a space."); + + cpBody* _body = root; + cpBody* next; + do { + next = _body.node.next; + + cpComponentNode node = {null, null, 0, 0.0f}; + _body.node = node; + cpArrayPush(space.bodies, _body); + + for(cpShape *shape=_body.shapesList; shape; shape=shape.next){ + cpSpaceHashRemove(space.staticShapes, shape, shape.hashid); + cpSpaceHashInsert(space.activeShapes, shape, shape.hashid, shape.bb); + } + } while((_body = next) != root); + + cpArrayDeleteObj(space.sleepingComponents, root); +} + +void +cpBodyActivate(cpBody *_body) +{ + // Reset the idle time even if it's not in a currently sleeping component + // Like a body resting on or jointed to a rogue body. + _body.node.idleTime = 0.0f; + componentActivate(componentNodeRoot(_body)); +} + +static void +mergeBodies(cpSpace *space, cpArray *components, cpArray *rogueBodies, cpBody *a, cpBody *b) +{ + // Don't merge with the static body + if(cpBodyIsStatic(a) || cpBodyIsStatic(b)) return; + + cpBody *a_root = componentNodeRoot(a); + cpBody *b_root = componentNodeRoot(b); + + cpBool a_sleep = cpBodyIsSleeping(a_root); + cpBool b_sleep = cpBodyIsSleeping(b_root); + + if(a_sleep && b_sleep){ + return; + } else if(a_sleep || b_sleep){ + componentActivate(a_root); + componentActivate(b_root); + } + + // Add any rogue bodies (bodies not added to the space) + if(!a.space) cpArrayPush(rogueBodies, a); + if(!b.space) cpArrayPush(rogueBodies, b); + + componentNodeMerge(a_root, b_root); +} + +static cpBool +componentActive(cpBody *root, cpFloat threshold) +{ + cpBody *_body = root; + cpBody *next; + do { + next = _body.node.next; + if(cpBodyIsRogue(_body) || _body.node.idleTime < threshold) return cpTrue; + } while((_body = next) != root); + + return cpFalse; +} + +static void +addToComponent(cpBody *_body, cpArray *components) +{ + // Check that the body is not already added to the component list + if(_body.node.next) return; + cpBody *root = componentNodeRoot(_body); + + cpBody *next = root.node.next; + if(!next){ + // If the root isn't part of a list yet, then it hasn't been + // added to the components list. Do that now. + cpArrayPush(components, root); + // Start the list + _body.node.next = root; + root.node.next = _body; + } else if(root != _body) { + // Splice in body after the root. + _body.node.next = next; + root.node.next = _body; + } +} + +// TODO this function needs more commenting. +void +cpSpaceProcessComponents(cpSpace *space, cpFloat dt) +{ + cpArray *bodies = space.bodies; + cpArray *newBodies = cpArrayNew(bodies.num); + cpArray *rogueBodies = cpArrayNew(16); + cpArray *arbiters = space.arbiters; + cpArray *constraints = space.constraints; + cpArray *components = cpArrayNew(bodies.num/8); + + cpFloat dv = space.idleSpeedThreshold; + cpFloat dvsq = (dv ? dv*dv : cpvdot(space.gravity, space.gravity)*dt*dt); + // update idling + for(int i=0; i<bodies.num; i++){ + cpBody *_body = cast(cpBody*)bodies.arr[i]; + + cpFloat thresh = (dvsq ? _body.m*dvsq : 0.0f); + _body.node.idleTime = (cpBodyKineticEnergy(_body) > thresh ? 0.0f : _body.node.idleTime + dt); + } + + // iterate graph edges and build forests + for(int i=0; i<arbiters.num; i++){ + cpArbiter *arb = cast(cpArbiter*)arbiters.arr[i]; + mergeBodies(space, components, rogueBodies, arb.private_a._body, arb.private_b._body); + } + for(int j=0; j<constraints.num; j++){ + cpConstraint *constraint = cast(cpConstraint *)constraints.arr[j]; + mergeBodies(space, components, rogueBodies, constraint.a, constraint.b); + } + + // iterate bodies and add them to their components + for(int i=0; i<bodies.num; i++) + addToComponent(cast(cpBody*)bodies.arr[i], components); + for(int i=0; i<rogueBodies.num; i++) + addToComponent(cast(cpBody*)rogueBodies.arr[i], components); + + // iterate components, copy or deactivate + for(int i=0; i<components.num; i++){ + cpBody *root = cast(cpBody*)components.arr[i]; + if(componentActive(root, space.sleepTimeThreshold)){ + cpBody *_body = root; + cpBody *next; + do { + next = _body.node.next; + + if(!cpBodyIsRogue(_body)) cpArrayPush(newBodies, _body); + _body.node.next = null; + _body.node.parent = null; + _body.node.rank = 0; + } while((_body = next) != root); + } else { + cpBody *_body = root; + cpBody *next; + do { + next = _body.node.next; + + for(cpShape *shape = _body.shapesList; shape; shape = shape.next){ + cpSpaceHashRemove(space.activeShapes, shape, shape.hashid); + cpSpaceHashInsert(space.staticShapes, shape, shape.hashid, shape.bb); + } + } while((_body = next) != root); + + cpArrayPush(space.sleepingComponents, root); + } + } + + space.bodies = newBodies; + cpArrayFree(bodies); + cpArrayFree(rogueBodies); + cpArrayFree(components); +} + +void +cpSpaceSleepBody(cpSpace *space, cpBody *_body){ + cpComponentNode node = {null, _body, 0, 0.0f}; + _body.node = node; + + for(cpShape *shape = _body.shapesList; shape; shape = shape.next){ + cpSpaceHashRemove(space.activeShapes, shape, shape.hashid); + + cpShapeCacheBB(shape); + cpSpaceHashInsert(space.staticShapes, shape, shape.hashid, shape.bb); + } + + cpArrayPush(space.sleepingComponents, _body); + cpArrayDeleteObj(space.bodies, _body); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpSpaceHash.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,611 @@ + +// written in the D programming language + +module chipmunkd.cpSpaceHash; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; +import chipmunkd.cpVect,chipmunkd.cpVect_h; +import chipmunkd.cpBB; +import chipmunkd.cpHashSet; +import chipmunkd.cpArray; +import chipmunkd.prime; + +// The spatial hash is Chipmunk's default (and currently only) spatial index type. +// Based on a chained hash table. + +// Used internally to track objects added to the hash +struct cpHandle{ + // Pointer to the object + void *obj; + // Retain count + int retain; + // Query stamp. Used to make sure two objects + // aren't identified twice in the same query. + cpTimestamp stamp; +} + +// Linked list element for in the chains. +struct cpSpaceHashBin{ + cpHandle *handle; + cpSpaceHashBin *next; +} + +// BBox callback. Called whenever the hash needs a bounding box from an object. +alias cpBB function(void *obj)cpSpaceHashBBFunc; + +struct cpSpaceHash{ + // Number of cells in the table. + int numcells; + // Dimentions of the cells. + cpFloat celldim; + + // BBox callback. + cpSpaceHashBBFunc bbfunc; + + // Hashset of the handles and the recycled ones. + cpHashSet *handleSet; + cpArray *pooledHandles; + + // The table and the recycled bins. + cpSpaceHashBin **table; + cpSpaceHashBin *pooledBins; + + // list of buffers to free on destruction. + cpArray *allocatedBuffers; + + // Incremented on each query. See cpHandle.stamp. + cpTimestamp stamp; +} + +////Basic allocation/destruction functions. +//cpSpaceHash *cpSpaceHashAlloc(void); +//cpSpaceHash *cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int cells, cpSpaceHashBBFunc bbfunc); +//cpSpaceHash *cpSpaceHashNew(cpFloat celldim, int cells, cpSpaceHashBBFunc bbfunc); +// +//void cpSpaceHashDestroy(cpSpaceHash *hash); +//void cpSpaceHashFree(cpSpaceHash *hash); +// +//// Resize the hashtable. (Does not rehash! You must call cpSpaceHashRehash() if needed.) +//void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells); +// +//// Add an object to the hash. +//void cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue id, cpBB _deprecated_ignored); +//// Remove an object from the hash. +//void cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue id); +// +//// Iterator function +alias void function(void *obj, void *data)cpSpaceHashIterator; +//// Iterate over the objects in the hash. +//void cpSpaceHashEach(cpSpaceHash *hash, cpSpaceHashIterator func, void *data); +// +//// Rehash the contents of the hash. +//void cpSpaceHashRehash(cpSpaceHash *hash); +//// Rehash only a specific object. +//void cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue id); +// +//// Query callback. +alias void function(void *obj1, void *obj2, void *data)cpSpaceHashQueryFunc; +//// Point query the hash. A reference to the query point is passed as obj1 to the query callback. +//void cpSpaceHashPointQuery(cpSpaceHash *hash, cpVect point, cpSpaceHashQueryFunc func, void *data); +//// Query the hash for a given BBox. +//void cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpaceHashQueryFunc func, void *data); +//// Run a query for the object, then insert it. (Optimized case) +//void cpSpaceHashQueryInsert(cpSpaceHash *hash, void *obj, cpBB bb, cpSpaceHashQueryFunc func, void *data); +//// Rehashes while querying for each object. (Optimized case) +//void cpSpaceHashQueryRehash(cpSpaceHash *hash, cpSpaceHashQueryFunc func, void *data); +// +//// Segment Query callback. +//// Return value is uesd for early exits of the query. +//// If while traversing the grid, the raytrace function detects that an entire grid cell is beyond the hit point, it will stop the trace. +alias cpFloat function(void *obj1, void *obj2, void *data)cpSpaceHashSegmentQueryFunc; +//void cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpaceHashSegmentQueryFunc func, void *data); +// + +static cpHandle* +cpHandleInit(cpHandle *hand, void *obj) +{ + hand.obj = obj; + hand.retain = 0; + hand.stamp = 0; + + return hand; +} + +static void cpHandleRetain(cpHandle *hand){hand.retain++;} + +static void +cpHandleRelease(cpHandle *hand, cpArray *pooledHandles) +{ + hand.retain--; + if(hand.retain == 0) cpArrayPush(pooledHandles, hand); +} + +cpSpaceHash* +cpSpaceHashAlloc() +{ + return cast(cpSpaceHash *)cpcalloc(1, cpSpaceHash.sizeof); +} + +// Frees the old table, and allocate a new one. +static void +cpSpaceHashAllocTable(cpSpaceHash *hash, int numcells) +{ + cpfree(hash.table); + + hash.numcells = numcells; + hash.table = cast(cpSpaceHashBin **)cpcalloc(numcells, (cpSpaceHashBin *).sizeof); +} + +// Equality function for the handleset. +static int handleSetEql(void *obj, cpHandle* hand){return (obj == hand.obj);} + +// Transformation function for the handleset. +static void * +handleSetTrans(void *obj, cpSpaceHash *hash) +{ + if(hash.pooledHandles.num == 0){ + // handle pool is exhausted, make more + int count = CP_BUFFER_BYTES/cpHandle.sizeof; + assert(count, "Buffer size is too small."); + + cpHandle *buffer = cast(cpHandle *)cpmalloc(CP_BUFFER_BYTES); + cpArrayPush(hash.allocatedBuffers, buffer); + + for(int i=0; i<count; i++) cpArrayPush(hash.pooledHandles, buffer + i); + } + + cpHandle *hand = cpHandleInit(cast(cpHandle *) cpArrayPop(hash.pooledHandles), obj); + cpHandleRetain(hand); + + return hand; +} + +cpSpaceHash* +cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpaceHashBBFunc bbfunc) +{ + cpSpaceHashAllocTable(hash, next_prime(numcells)); + hash.celldim = celldim; + hash.bbfunc = bbfunc; + + hash.handleSet = cpHashSetNew(0, cast(cpHashSetEqlFunc)&handleSetEql, cast(cpHashSetTransFunc)&handleSetTrans); + hash.pooledHandles = cpArrayNew(0); + + hash.pooledBins = null; + hash.allocatedBuffers = cpArrayNew(0); + + hash.stamp = 1; + + return hash; +} + +cpSpaceHash* +cpSpaceHashNew(cpFloat celldim, int cells, cpSpaceHashBBFunc bbfunc) +{ + return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc); +} + +static void +recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin) +{ + bin.next = hash.pooledBins; + hash.pooledBins = bin; +} + +static void +clearHashCell(cpSpaceHash *hash, int idx) +{ + cpSpaceHashBin *bin = hash.table[idx]; + while(bin){ + cpSpaceHashBin *next = bin.next; + + cpHandleRelease(bin.handle, hash.pooledHandles); + recycleBin(hash, bin); + + bin = next; + } + + hash.table[idx] = null; +} + +// Clear all cells in the hashtable. +static void +clearHash(cpSpaceHash *hash) +{ + for(int i=0; i<hash.numcells; i++) + clearHashCell(hash, i); +} + +static void freeWrap(void *ptr, void *unused){cpfree(ptr);} + +void +cpSpaceHashDestroy(cpSpaceHash *hash) +{ + clearHash(hash); + + cpHashSetFree(hash.handleSet); + + cpArrayEach(hash.allocatedBuffers, &freeWrap, null); + cpArrayFree(hash.allocatedBuffers); + cpArrayFree(hash.pooledHandles); + + cpfree(hash.table); +} + +void +cpSpaceHashFree(cpSpaceHash *hash) +{ + if(hash){ + cpSpaceHashDestroy(hash); + cpfree(hash); + } +} + +void +cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells) +{ + // Clear the hash to release the old handle locks. + clearHash(hash); + + hash.celldim = celldim; + cpSpaceHashAllocTable(hash, next_prime(numcells)); +} + +// Return true if the chain contains the handle. +static cpBool +containsHandle(cpSpaceHashBin *bin, cpHandle *hand) +{ + while(bin){ + if(bin.handle == hand) return cpTrue; + bin = bin.next; + } + + return cpFalse; +} + +// Get a recycled or new bin. +static cpSpaceHashBin * +getEmptyBin(cpSpaceHash *hash) +{ + cpSpaceHashBin *bin = hash.pooledBins; + + if(bin){ + hash.pooledBins = bin.next; + return bin; + } else { + // Pool is exhausted, make more + int count = CP_BUFFER_BYTES/cpSpaceHashBin.sizeof; + assert(count, "Buffer size is too small."); + + cpSpaceHashBin *buffer = cast(cpSpaceHashBin *)cpmalloc(CP_BUFFER_BYTES); + cpArrayPush(hash.allocatedBuffers, buffer); + + // push all but the first one, return the first instead + for(int i=1; i<count; i++) recycleBin(hash, buffer + i); + return buffer; + } +} + +// The hash function itself. +static cpHashValue +hash_func(cpHashValue x, cpHashValue y, cpHashValue n) +{ + return cast(cpHashValue)((x*1640531513uL ^ y*2654435789uL) % n); +} + +// Much faster than (int)floor(f) +// Profiling showed floor() to be a sizable performance hog +static int +floor_int(cpFloat f) +{ + int i = cast(int)f; + return (f < 0.0f && f != i ? i - 1 : i); +} + +static void +hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb) +{ + // Find the dimensions in cell coordinates. + cpFloat dim = hash.celldim; + int l = floor_int(bb.l/dim); // Fix by ShiftZ + int r = floor_int(bb.r/dim); + int b = floor_int(bb.b/dim); + int t = floor_int(bb.t/dim); + + int n = hash.numcells; + for(int i=l; i<=r; i++){ + for(int j=b; j<=t; j++){ + int idx = hash_func(i,j,n); + cpSpaceHashBin *bin = hash.table[idx]; + + // Don't add an object twice to the same cell. + if(containsHandle(bin, hand)) continue; + + cpHandleRetain(hand); + // Insert a new bin for the handle in this cell. + cpSpaceHashBin *newBin = getEmptyBin(hash); + newBin.handle = hand; + newBin.next = bin; + hash.table[idx] = newBin; + } + } +} + +void +cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid, cpBB _deprecated_unused) +{ + cpHandle *hand = cast(cpHandle *)cpHashSetInsert(hash.handleSet, hashid, obj, hash); + hashHandle(hash, hand, hash.bbfunc(obj)); +} + +void +cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid) +{ + cpHandle *hand = cast(cpHandle *)cpHashSetRemove(hash.handleSet, hashid, obj); + + if(hand){ + hand.obj = null; + cpHandleRelease(hand, hash.pooledHandles); + + cpSpaceHashInsert(hash, obj, hashid, cpBBNew(0.0f, 0.0f, 0.0f, 0.0f)); + } +} + +static void handleRehashHelper(cpHandle *hand, cpSpaceHash *hash){hashHandle(hash, hand, hash.bbfunc(hand.obj));} + +void +cpSpaceHashRehash(cpSpaceHash *hash) +{ + clearHash(hash); + cpHashSetEach(hash.handleSet, cast(cpHashSetIterFunc)&handleRehashHelper, hash); +} + +void +cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid) +{ + cpHandle *hand = cast(cpHandle *)cpHashSetRemove(hash.handleSet, hashid, obj); + + if(hand){ + hand.obj = null; + cpHandleRelease(hand, hash.pooledHandles); + } +} + +struct eachPair { + cpSpaceHashIterator func; + void *data; +} + +static void eachHelper(cpHandle *hand, eachPair *pair){pair.func(hand.obj, pair.data);} + +// Iterate over the objects in the spatial hash. +void +cpSpaceHashEach(cpSpaceHash *hash, cpSpaceHashIterator func, void *data) +{ + eachPair pair = {func, data}; + cpHashSetEach(hash.handleSet, cast(cpHashSetIterFunc)&eachHelper, &pair); +} + +static void +removeOrphanedHandles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr) +{ + cpSpaceHashBin *bin = *bin_ptr; + while(bin){ + cpHandle *hand = bin.handle; + cpSpaceHashBin *next = bin.next; + + if(!hand.obj){ + // orphaned handle, unlink and recycle the bin + (*bin_ptr) = bin.next; + recycleBin(hash, bin); + + cpHandleRelease(hand, hash.pooledHandles); + } else { + bin_ptr = &bin.next; + } + + bin = next; + } +} + +// Calls the callback function for the objects in a given chain. +static void +query(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpaceHashQueryFunc func, void *data) +{ + restart: + for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin.next){ + cpHandle *hand = bin.handle; + void *other = hand.obj; + + if(hand.stamp == hash.stamp || obj == other){ + continue; + } else if(other){ + func(obj, other, data); + hand.stamp = hash.stamp; + } else { + // The object for this handle has been removed + // cleanup this cell and restart the query + removeOrphanedHandles(hash, bin_ptr); + goto restart; // GCC not smart enough/able to tail call an inlined function. + } + } +} + +void +cpSpaceHashPointQuery(cpSpaceHash *hash, cpVect point, cpSpaceHashQueryFunc func, void *data) +{ + cpFloat dim = hash.celldim; + int idx = hash_func(floor_int(point.x/dim), floor_int(point.y/dim), hash.numcells); // Fix by ShiftZ + + query(hash, &hash.table[idx], &point, func, data); + hash.stamp++; +} + +void +cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpaceHashQueryFunc func, void *data) +{ + // Get the dimensions in cell coordinates. + cpFloat dim = hash.celldim; + int l = floor_int(bb.l/dim); // Fix by ShiftZ + int r = floor_int(bb.r/dim); + int b = floor_int(bb.b/dim); + int t = floor_int(bb.t/dim); + + int n = hash.numcells; + cpSpaceHashBin **table = hash.table; + + // Iterate over the cells and query them. + for(int i=l; i<=r; i++){ + for(int j=b; j<=t; j++){ + query(hash, &table[hash_func(i,j,n)], obj, func, data); + } + } + + hash.stamp++; +} + +// Similar to struct eachPair above. +struct queryRehashPair { + cpSpaceHash *hash; + cpSpaceHashQueryFunc func; + void *data; +} + +// Hashset iterator func used with cpSpaceHashQueryRehash(). +static void +handleQueryRehashHelper(void *elt, void *data) +{ + cpHandle *hand = cast(cpHandle *)elt; + + // Unpack the user callback data. + queryRehashPair *pair = cast(queryRehashPair *)data; + cpSpaceHash *hash = pair.hash; + cpSpaceHashQueryFunc func = pair.func; + + cpFloat dim = hash.celldim; + int n = hash.numcells; + + void *obj = hand.obj; + cpBB bb = hash.bbfunc(obj); + + int l = floor_int(bb.l/dim); + int r = floor_int(bb.r/dim); + int b = floor_int(bb.b/dim); + int t = floor_int(bb.t/dim); + + cpSpaceHashBin **table = hash.table; + + for(int i=l; i<=r; i++){ + for(int j=b; j<=t; j++){ + int idx = hash_func(i,j,n); + cpSpaceHashBin *bin = table[idx]; + + if(containsHandle(bin, hand)) continue; + + cpHandleRetain(hand); // this MUST be done first in case the object is removed in func() + query(hash, &bin, obj, func, pair.data); + + cpSpaceHashBin *newBin = getEmptyBin(hash); + newBin.handle = hand; + newBin.next = bin; + table[idx] = newBin; + } + } + + // Increment the stamp for each object hashed. + hash.stamp++; +} + +void +cpSpaceHashQueryRehash(cpSpaceHash *hash, cpSpaceHashQueryFunc func, void *data) +{ + clearHash(hash); + + queryRehashPair pair = {hash, func, data}; + cpHashSetEach(hash.handleSet, &handleQueryRehashHelper, &pair); +} + +static cpFloat +segmentQuery(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpaceHashSegmentQueryFunc func, void *data) +{ + cpFloat t = 1.0f; + + restart: + for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin.next){ + cpHandle *hand = bin.handle; + void *other = hand.obj; + + // Skip over certain conditions + if(hand.stamp == hash.stamp){ + continue; + } else if(other){ + t = cpfmin(t, func(obj, other, data)); + hand.stamp = hash.stamp; + } else { + // The object for this handle has been removed + // cleanup this cell and restart the query + removeOrphanedHandles(hash, bin_ptr); + goto restart; // GCC not smart enough/able to tail call an inlined function. + } + } + + return t; +} + +// modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html +void cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpaceHashSegmentQueryFunc func, void *data) +{ + a = cpvmult(a, 1.0f/hash.celldim); + b = cpvmult(b, 1.0f/hash.celldim); + + int cell_x = floor_int(a.x), cell_y = floor_int(a.y); + + cpFloat t = 0; + + int x_inc, y_inc; + cpFloat temp_v, temp_h; + + if (b.x > a.x){ + x_inc = 1; + temp_h = (cpffloor(a.x + 1.0f) - a.x); + } else { + x_inc = -1; + temp_h = (a.x - cpffloor(a.x)); + } + + if (b.y > a.y){ + y_inc = 1; + temp_v = (cpffloor(a.y + 1.0f) - a.y); + } else { + y_inc = -1; + temp_v = (a.y - cpffloor(a.y)); + } + + // Division by zero is *very* slow on ARM + cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y); + cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY); + + // fix NANs in horizontal directions + cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx); + cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy); + + cpSpaceHashBin **table = hash.table; + + int n = hash.numcells; + while(t < t_exit){ + int idx = hash_func(cell_x, cell_y, n); + t_exit = cpfmin(t_exit, segmentQuery(hash, &table[idx], obj, func, data)); + + if (next_v < next_h){ + cell_y += y_inc; + t = next_v; + next_v += dt_dy; + } else { + cell_x += x_inc; + t = next_h; + next_h += dt_dx; + } + } + + hash.stamp++; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpSpaceQuery.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,167 @@ + +// written in the D programming language + +module chipmunkd.cpSpaceQuery; + +import chipmunkd.chipmunk; +import chipmunkd.chipmunk_types_h; + +struct pointQueryContext { + cpLayers layers; + cpGroup group; + cpSpacePointQueryFunc func; + void *data; +} + +static void +pointQueryHelper(cpVect *point, cpShape *shape, pointQueryContext *context) +{ + if( + !(shape.group && context.group == shape.group) && (context.layers&shape.layers) && + cpShapePointQuery(shape, *point) + ){ + context.func(shape, context.data); + } +} + +void +cpSpacePointQuery(cpSpace *space, cpVect point, cpLayers layers, cpGroup group, cpSpacePointQueryFunc func, void *data) +{ + pointQueryContext context = {layers, group, func, data}; + cpSpaceHashPointQuery(space.activeShapes, point, cast(cpSpaceHashQueryFunc)&pointQueryHelper, &context); + cpSpaceHashPointQuery(space.staticShapes, point, cast(cpSpaceHashQueryFunc)&pointQueryHelper, &context); +} + +static void +rememberLastPointQuery(cpShape *shape, cpShape **outShape) +{ + if(!shape.sensor) *outShape = shape; +} + +cpShape * +cpSpacePointQueryFirst(cpSpace *space, cpVect point, cpLayers layers, cpGroup group) +{ + cpShape *shape = null; + cpSpacePointQuery(space, point, layers, group, cast(cpSpacePointQueryFunc)&rememberLastPointQuery, &shape); + + return shape; +} + +void +cpSpaceEachBody(cpSpace *space, cpSpaceBodyIterator func, void *data) +{ + cpArray *bodies = space.bodies; + + for(int i=0; i<bodies.num; i++) + func(cast(cpBody *)bodies.arr[i], data); +} + +//#pragma mark Segment Query Functions + +struct segQueryContext { + cpVect start, end; + cpLayers layers; + cpGroup group; + cpSpaceSegmentQueryFunc func; +} + +static cpFloat +segQueryFunc(segQueryContext *context, cpShape *shape, void *data) +{ + cpSegmentQueryInfo info; + + if( + !(shape.group && context.group == shape.group) && (context.layers&shape.layers) && + cpShapeSegmentQuery(shape, context.start, context.end, &info) + ){ + context.func(shape, info.t, info.n, data); + } + + return 1.0f; +} + +void +cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpLayers layers, cpGroup group, cpSpaceSegmentQueryFunc func, void *data) +{ + segQueryContext context = { + start, end, + layers, group, + func, + }; + + cpSpaceHashSegmentQuery(space.staticShapes, &context, start, end, 1.0f, cast(cpSpaceHashSegmentQueryFunc)&segQueryFunc, data); + cpSpaceHashSegmentQuery(space.activeShapes, &context, start, end, 1.0f, cast(cpSpaceHashSegmentQueryFunc)&segQueryFunc, data); +} + +struct segQueryFirstContext { + cpVect start, end; + cpLayers layers; + cpGroup group; +} + +static cpFloat +segQueryFirst(segQueryFirstContext *context, cpShape *shape, cpSegmentQueryInfo *_out) +{ + cpSegmentQueryInfo info; + + if( + !(shape.group && context.group == shape.group) && + (context.layers&shape.layers) && + !shape.sensor && + cpShapeSegmentQuery(shape, context.start, context.end, &info) && + info.t < _out.t + ){ + *_out = info; + } + + return _out.t; +} + +cpShape * +cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpLayers layers, cpGroup group, cpSegmentQueryInfo *_out) +{ + cpSegmentQueryInfo info = {null, 1.0f, cpvzero}; + if(_out){ + (*_out) = info; + } else { + _out = &info; + } + + segQueryFirstContext context = { + start, end, + layers, group + }; + + cpSpaceHashSegmentQuery(space.staticShapes, &context, start, end, 1.0f, cast(cpSpaceHashSegmentQueryFunc)&segQueryFirst, _out); + cpSpaceHashSegmentQuery(space.activeShapes, &context, start, end, _out.t, cast(cpSpaceHashSegmentQueryFunc)&segQueryFirst, _out); + + return _out.shape; +} + +//#pragma mark BB Query functions + +struct bbQueryContext { + cpLayers layers; + cpGroup group; + cpSpaceBBQueryFunc func; + void *data; +} + +static void +bbQueryHelper(cpBB *bb, cpShape *shape, bbQueryContext *context) +{ + if( + !(shape.group && context.group == shape.group) && (context.layers&shape.layers) && + cpBBintersects(*bb, shape.bb) + ){ + context.func(shape, context.data); + } +} + +void +cpSpaceBBQuery(cpSpace *space, cpBB bb, cpLayers layers, cpGroup group, cpSpaceBBQueryFunc func, void *data) +{ + bbQueryContext context = {layers, group, func, data}; + cpSpaceHashQuery(space.activeShapes, &bb, bb, cast(cpSpaceHashQueryFunc)&bbQueryHelper, &context); + cpSpaceHashQuery(space.staticShapes, &bb, bb, cast(cpSpaceHashQueryFunc)&bbQueryHelper, &context); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpSpaceStep.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,376 @@ + +// written in the D programming language + +module chipmunkd.cpSpaceStep; + +import chipmunkd.chipmunk; +//import chipmunkd.chipmunk_types_h; +//import chipmunkd.cpVect,chipmunkd.cpVect_h; +//import chipmunkd.cpHashSet; +//import chipmunkd.cpCollision; +//import chipmunkd.cpBody; +//import chipmunkd.cpArray; +//import chipmunkd.cpShape; +//import chipmunkd.cpBB; +//import chipmunkd.cpArbiter; +//import chipmunkd.cpSpaceHash; +//import chipmunkd.constraints.cpConstraint; +//import chipmunkd.cpSpaceQuery; +// +//#pragma mark Post Step Callback Functions + +struct postStepCallback { + cpPostStepFunc func; + void *obj; + void *data; +} + +static cpBool +postStepFuncSetEql(postStepCallback *a, postStepCallback *b){ + return a.obj == b.obj; +} + +static void * +postStepFuncSetTrans(postStepCallback *callback, void *ignored) +{ + postStepCallback *value = cast(postStepCallback *)cpmalloc(postStepCallback.sizeof); + (*value) = (*callback); + + return value; +} + +void +cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *obj, void *data) +{ + if(!space.postStepCallbacks){ + space.postStepCallbacks = cpHashSetNew(0, cast(cpHashSetEqlFunc)&postStepFuncSetEql, cast(cpHashSetTransFunc)&postStepFuncSetTrans); + } + + postStepCallback callback = {func, obj, data}; + cpHashSetInsert(space.postStepCallbacks, cast(cpHashValue)cast(size_t)obj, &callback, null); +} + +//#pragma mark Contact Buffer Functions + +enum CP_CONTACTS_BUFFER_SIZE = ((CP_BUFFER_BYTES - cpContactBufferHeader.sizeof)/cpContact.sizeof); + +struct cpContactBuffer { + cpContactBufferHeader header; + cpContact[CP_CONTACTS_BUFFER_SIZE] contacts; +} + +static cpContactBufferHeader * +cpSpaceAllocContactBuffer(cpSpace *space) +{ + cpContactBuffer *buffer = cast(cpContactBuffer *)cpmalloc(cpContactBuffer.sizeof); + cpArrayPush(space.allocatedBuffers, buffer); + return cast(cpContactBufferHeader *)buffer; +} + +static cpContactBufferHeader * +cpContactBufferHeaderInit(cpContactBufferHeader *header, cpTimestamp stamp, cpContactBufferHeader *splice) +{ + header.stamp = stamp; + header.next = (splice ? splice.next : header); + header.numContacts = 0; + + return header; +} + +static void +cpSpacePushFreshContactBuffer(cpSpace *space) +{ + cpTimestamp stamp = space.stamp; + + cpContactBufferHeader *head = space.contactBuffersHead; + + if(!head){ + // No buffers have been allocated, make one + space.contactBuffersHead = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, null); + } else if(stamp - head.next.stamp > cp_contact_persistence){ + // The tail buffer is available, rotate the ring + cpContactBufferHeader *tail = head.next; + space.contactBuffersHead = cpContactBufferHeaderInit(tail, stamp, tail); + } else { + // Allocate a new buffer and push it into the ring + cpContactBufferHeader *buffer = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, head); + space.contactBuffersHead = head.next = buffer; + } +} + + +static cpContact * +cpContactBufferGetArray(cpSpace *space) +{ + if(space.contactBuffersHead.numContacts + CP_MAX_CONTACTS_PER_ARBITER > CP_CONTACTS_BUFFER_SIZE){ + // contact buffer could overflow on the next collision, push a fresh one. + cpSpacePushFreshContactBuffer(space); + } + + cpContactBufferHeader *head = space.contactBuffersHead; + return &(cast(cpContactBuffer *)head).contacts[head.numContacts]; //TODO: check if this works +} + +static void +cpSpacePushContacts(cpSpace *space, int count){ + assert(count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error, too many contact point overflow!"); + space.contactBuffersHead.numContacts += count; +} + +static void +cpSpacePopContacts(cpSpace *space, int count){ + space.contactBuffersHead.numContacts -= count; +} + +//#pragma mark Collision Detection Functions + +static cpBool +queryReject(cpShape *a, cpShape *b) +{ + return + // BBoxes must overlap + !cpBBintersects(a.bb, b.bb) + // Don't collide shapes attached to the same body. + || a._body == b._body + // Don't collide objects in the same non-zero group + || (a.group && a.group == b.group) + // Don't collide objects that don't share at least on layer. + || !(a.layers & b.layers); +} + +// Callback from the spatial hash. +static void +queryFunc(cpShape *a, cpShape *b, cpSpace *space) +{ + // Reject any of the simple cases + if(queryReject(a,b)) return; + + // Find the collision pair function for the shapes. + struct tmp{cpCollisionType a, b;} tmp ids = {a.collision_type, b.collision_type}; + cpHashValue collHashID = CP_HASH_PAIR(a.collision_type, b.collision_type); + cpCollisionHandler *handler = cast(cpCollisionHandler *)cpHashSetFind(space.collFuncSet, collHashID, &ids); + + cpBool sensor = a.sensor || b.sensor; + if(sensor && handler == &space.defaultHandler) return; + + // Shape 'a' should have the lower shape type. (required by cpCollideShapes() ) + if(a.klass.type > b.klass.type){ + cpShape *temp = a; + a = b; + b = temp; + } + + // Narrow-phase collision detection. + cpContact *contacts = cpContactBufferGetArray(space); + int numContacts = cpCollideShapes(a, b, contacts); + if(!numContacts) return; // Shapes are not colliding. + cpSpacePushContacts(space, numContacts); + + // Get an arbiter from space.contactSet for the two shapes. + // This is where the persistant contact magic comes from. + cpShape *shape_pair[] = [a, b]; + cpHashValue arbHashID = CP_HASH_PAIR(cast(size_t)a, cast(size_t)b); + cpArbiter *arb = cast(cpArbiter *)cpHashSetInsert(space.contactSet, arbHashID, shape_pair.ptr, space); + cpArbiterUpdate(arb, contacts, numContacts, handler, a, b); // retains the contacts array + + // Call the begin function first if it's the first step + if(arb.state == cpArbiterState.cpArbiterStateFirstColl && !handler.begin(arb, space, handler.data)){ + cpArbiterIgnore(arb); // permanently ignore the collision until separation + } + + if( + // Ignore the arbiter if it has been flagged + (arb.state != cpArbiterState.cpArbiterStateIgnore) && + // Call preSolve + handler.preSolve(arb, space, handler.data) && + // Process, but don't add collisions for sensors. + !sensor + ){ + cpArrayPush(space.arbiters, arb); + } else { + cpSpacePopContacts(space, numContacts); + + arb.contacts = null; + arb.numContacts = 0; + + // Normally arbiters are set as used after calling the post-step callback. + // However, post-step callbacks are not called for sensors or arbiters rejected from pre-solve. + if(arb.state != cpArbiterState.cpArbiterStateIgnore) arb.state = cpArbiterState.cpArbiterStateNormal; + } + + // Time stamp the arbiter so we know it was used recently. + arb.stamp = space.stamp; +} + +// Iterator for active/static hash collisions. +static void +active2staticIter(cpShape *shape, cpSpace *space) +{ + cpSpaceHashQuery(space.staticShapes, shape, shape.bb, cast(cpSpaceHashQueryFunc)&queryFunc, space); +} + +// Hashset filter func to throw away old arbiters. +static cpBool +contactSetFilter(cpArbiter *arb, cpSpace *space) +{ + if(space.sleepTimeThreshold != INFINITY){ + cpBody *a = arb.private_a._body; + cpBody *b = arb.private_b._body; + + // both bodies are either static or sleeping + cpBool sleepingNow = + (cpBodyIsStatic(a) || cpBodyIsSleeping(a)) && + (cpBodyIsStatic(b) || cpBodyIsSleeping(b)); + + if(sleepingNow){ + arb.state = cpArbiterState.cpArbiterStateSleep; + return cpTrue; + } else if(arb.state == cpArbiterState.cpArbiterStateSleep){ + // wake up the arbiter and continue as normal + arb.state = cpArbiterState.cpArbiterStateNormal; + // TODO is it possible that cpArbiterStateIgnore should be set here instead? + } + } + + cpTimestamp ticks = space.stamp - arb.stamp; + + // was used last frame, but not this one + if(ticks >= 1 && arb.state != cpArbiterState.cpArbiterStateCached){ + arb.handler.separate(arb, space, arb.handler.data); + arb.state = cpArbiterState.cpArbiterStateCached; + } + + if(ticks >= cp_contact_persistence){ + arb.contacts = null; + arb.numContacts = 0; + + cpArrayPush(space.pooledArbiters, arb); + return cpFalse; + } + + return cpTrue; +} + +// Hashset filter func to call and throw away post step callbacks. +static void +postStepCallbackSetIter(postStepCallback *callback, cpSpace *space) +{ + callback.func(space, callback.obj, callback.data); + cpfree(callback); +} + +//#pragma mark All Important cpSpaceStep() Function + +//void cpSpaceProcessComponents(cpSpace *space, cpFloat dt); + +static void updateBBCache(cpShape *shape, void *unused){cpShapeCacheBB(shape);} + +void +cpSpaceStep(cpSpace *space, cpFloat dt) +{ + if(!dt) return; // don't step if the timestep is 0! + + cpFloat dt_inv = 1.0f/dt; + + cpArray *bodies = space.bodies; + cpArray *constraints = space.constraints; + + space.locked = cpTrue; + + // Empty the arbiter list. + space.arbiters.num = 0; + + // Integrate positions. + for(int i=0; i<bodies.num; i++){ + cpBody *_body = cast(cpBody *)bodies.arr[i]; + _body.position_func(_body, dt); + } + + // Pre-cache BBoxes and shape data. + cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&updateBBCache, null); + + // Collide! + cpSpacePushFreshContactBuffer(space); + if(space.staticShapes.handleSet.entries) + cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&active2staticIter, space); + cpSpaceHashQueryRehash(space.activeShapes, cast(cpSpaceHashQueryFunc)&queryFunc, space); + + // If body sleeping is enabled, do that now. + if(space.sleepTimeThreshold != INFINITY){ + cpSpaceProcessComponents(space, dt); + bodies = space.bodies; // rebuilt by processContactComponents() + } + + // Clear out old cached arbiters and dispatch untouch functions + cpHashSetFilter(space.contactSet, cast(cpHashSetFilterFunc)&contactSetFilter, space); + + // Prestep the arbiters. + cpArray *arbiters = space.arbiters; + for(int i=0; i<arbiters.num; i++) + cpArbiterPreStep(cast(cpArbiter *)arbiters.arr[i], dt_inv); + + // Prestep the constraints. + for(int i=0; i<constraints.num; i++){ + cpConstraint *constraint = cast(cpConstraint *)constraints.arr[i]; + constraint.klass.preStep(constraint, dt, dt_inv); + } + + for(int i=0; i<space.elasticIterations; i++){ + for(int j=0; j<arbiters.num; j++) + cpArbiterApplyImpulse(cast(cpArbiter *)arbiters.arr[j], 1.0f); + + for(int j=0; j<constraints.num; j++){ + cpConstraint *constraint = cast(cpConstraint *)constraints.arr[j]; + constraint.klass.applyImpulse(constraint); + } + } + + // Integrate velocities. + cpFloat damping = cpfpow(1.0f/space.damping, -dt); + for(int i=0; i<bodies.num; i++){ + cpBody *_body = cast(cpBody *)bodies.arr[i]; + _body.velocity_func(_body, space.gravity, damping, dt); + } + + for(int i=0; i<arbiters.num; i++) + cpArbiterApplyCachedImpulse(cast(cpArbiter *)arbiters.arr[i]); + + // run the old-style elastic solver if elastic iterations are disabled + cpFloat elasticCoef = (space.elasticIterations ? 0.0f : 1.0f); + + // Run the impulse solver. + for(int i=0; i<space.iterations; i++){ + for(int j=0; j<arbiters.num; j++) + cpArbiterApplyImpulse(cast(cpArbiter *)arbiters.arr[j], elasticCoef); + + for(int j=0; j<constraints.num; j++){ + cpConstraint *constraint = cast(cpConstraint *)constraints.arr[j]; + constraint.klass.applyImpulse(constraint); + } + } + + space.locked = cpFalse; + + // run the post solve callbacks + for(int i=0; i<arbiters.num; i++){ + cpArbiter *arb = cast(cpArbiter *) arbiters.arr[i]; + + cpCollisionHandler *handler = arb.handler; + handler.postSolve(arb, space, handler.data); + + arb.state = cpArbiterState.cpArbiterStateNormal; + } + + // Run the post step callbacks + // Loop because post step callbacks may create more post step callbacks + while(space.postStepCallbacks){ + cpHashSet *callbacks = space.postStepCallbacks; + space.postStepCallbacks = null; + + cpHashSetEach(callbacks, cast(cpHashSetIterFunc)&postStepCallbackSetIter, space); + cpHashSetFree(callbacks); + } + + // Increment the stamp. + space.stamp++; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpVect.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,53 @@ + +// written in the D programming language + +module chipmunkd.cpVect; + +import chipmunkd.cpVect_h; +import chipmunkd.chipmunk_types_h; + +cpFloat +cpvlength(const cpVect v) +{ + return cpfsqrt( cpvdot(v, v) ); +} + +cpVect +cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t) +{ + cpFloat omega = cpfacos(cpvdot(v1, v2)); + + if(omega){ + cpFloat denom = 1.0f/cpfsin(omega); + return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom)); + } else { + return v1; + } +} + +cpVect +cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a) +{ + cpFloat angle = cpfacos(cpvdot(v1, v2)); + return cpvslerp(v1, v2, cpfmin(a, angle)/angle); +} + +cpVect +cpvforangle(const cpFloat a) +{ + return cpv(cpfcos(a), cpfsin(a)); +} + +cpFloat +cpvtoangle(const cpVect v) +{ + return cpfatan2(v.y, v.x); +} + +//char* +//cpvstr(const cpVect v) +//{ +// static char str[256]; +// sprintf(str, "(% .3f, % .3f)", v.x, v.y); +// return str; +//} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/cpVect_h.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,194 @@ + +// written in the D programming language + +module chipmunkd.cpVect_h; + +import chipmunkd.cpVect; +import chipmunkd.chipmunk_types_h; + +/// Constant for the zero vector. +static const cpVect cpvzero = {0.0f,0.0f}; + +/// Convenience constructor for cpVect structs. +static cpVect +cpv(const cpFloat x, const cpFloat y) +{ + cpVect v = {x, y}; + return v; +} + +// non-inlined functions + +///// Returns the length of v. +//cpFloat cpvlength(const cpVect v); +// +///// Spherical linearly interpolate between v1 and v2. +//cpVect cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t); +// +///// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians +//cpVect cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a); +// +///// Returns the unit length vector for the given angle (in radians). +//cpVect cpvforangle(const cpFloat a); +// +///// Returns the angular direction v is pointing in (in radians). +//cpFloat cpvtoangle(const cpVect v); + +/** + Returns a string representation of v. Intended mostly for debugging purposes and not production use. + + @attention The string points to a static local and is reset every time the function is called. + If you want to print more than one vector you will have to split up your printing onto separate lines. +*/ +//char *cpvstr(const cpVect v); + +/// Check if two vectors are equal. (Be careful when comparing floating point numbers!) +static cpBool +cpveql(const cpVect v1, const cpVect v2) +{ + return (v1.x == v2.x && v1.y == v2.y); +} + +/// Add two vectors +static cpVect +cpvadd(const cpVect v1, const cpVect v2) +{ + return cpv(v1.x + v2.x, v1.y + v2.y); +} + +/// Negate a vector. +static cpVect +cpvneg(const cpVect v) +{ + return cpv(-v.x, -v.y); +} + +/// Subtract two vectors. +static cpVect +cpvsub(const cpVect v1, const cpVect v2) +{ + return cpv(v1.x - v2.x, v1.y - v2.y); +} + +/// Scalar multiplication. +static cpVect +cpvmult(const cpVect v, const cpFloat s) +{ + return cpv(v.x*s, v.y*s); +} + +/// Vector dot product. +static cpFloat +cpvdot(const cpVect v1, const cpVect v2) +{ + return v1.x*v2.x + v1.y*v2.y; +} + +/** + 2D vector cross product analog. + The cross product of 2D vectors results in a 3D vector with only a z component. + This function returns the magnitude of the z value. +*/ +static cpFloat +cpvcross(const cpVect v1, const cpVect v2) +{ + return v1.x*v2.y - v1.y*v2.x; +} + +/// Returns a perpendicular vector. (90 degree rotation) +static cpVect +cpvperp(const cpVect v) +{ + return cpv(-v.y, v.x); +} + +/// Returns a perpendicular vector. (-90 degree rotation) +static cpVect +cpvrperp(const cpVect v) +{ + return cpv(v.y, -v.x); +} + +/// Returns the vector projection of v1 onto v2. +static cpVect +cpvproject(const cpVect v1, const cpVect v2) +{ + return cpvmult(v2, cpvdot(v1, v2)/cpvdot(v2, v2)); +} + +/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. +static cpVect +cpvrotate(const cpVect v1, const cpVect v2) +{ + return cpv(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x); +} + +/// Inverse of cpvrotate(). +static cpVect +cpvunrotate(const cpVect v1, const cpVect v2) +{ + return cpv(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y); +} + +/// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. +static cpFloat +cpvlengthsq(const cpVect v) +{ + return cpvdot(v, v); +} + +/// Linearly interpolate between v1 and v2. +static cpVect +cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t) +{ + return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t)); +} + +/// Returns a normalized copy of v. +static cpVect +cpvnormalize(const cpVect v) +{ + return cpvmult(v, 1.0f/cpvlength(v)); +} + +/// Returns a normalized copy of v or cpvzero if v was already cpvzero. Protects against divide by zero errors. +static cpVect +cpvnormalize_safe(const cpVect v) +{ + return (v.x == 0.0f && v.y == 0.0f ? cpvzero : cpvnormalize(v)); +} + +/// Clamp v to length len. +static cpVect +cpvclamp(const cpVect v, const cpFloat len) +{ + return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v; +} + +/// Linearly interpolate between v1 towards v2 by distance d. +static cpVect +cpvlerpconst(cpVect v1, cpVect v2, cpFloat d) +{ + return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d)); +} + +/// Returns the distance between v1 and v2. +static cpFloat +cpvdist(const cpVect v1, const cpVect v2) +{ + return cpvlength(cpvsub(v1, v2)); +} + +/// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances. +static cpFloat +cpvdistsq(const cpVect v1, const cpVect v2) +{ + return cpvlengthsq(cpvsub(v1, v2)); +} + +/// Returns true if the distance between v1 and v2 is less than dist. +static cpBool +cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist) +{ + return cpvdistsq(v1, v2) < dist*dist; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/chipmunkd/prime.d Thu Dec 02 02:11:26 2010 +0100 @@ -0,0 +1,52 @@ + +// written in the D programming language + +module chipmunkd.prime; + +// Used for resizing hash tables. +// Values approximately double. +// http://planetmath.org/encyclopedia/GoodHashTablePrimes.html +enum int primes[] = [ + 5, + 13, + 23, + 47, + 97, + 193, + 389, + 769, + 1543, + 3079, + 6151, + 12289, + 24593, + 49157, + 98317, + 196613, + 393241, + 786433, + 1572869, + 3145739, + 6291469, + 12582917, + 25165843, + 50331653, + 100663319, + 201326611, + 402653189, + 805306457, + 1610612741, + 0, +]; + +static int +next_prime(int n) +{ + int i = 0; + while(n > primes[i]){ + i++; + assert(primes[i]!=0, "Tried to resize a hash table to a size greater than 1610612741 O_o"); // realistically this should never happen + } + + return primes[i]; +} \ No newline at end of file