view trunk/chipmunkd/cpArbiter.d @ 23:4ceef5833c8c

updated to chipmunk 5.3.3
author Extrawurst
date Fri, 10 Dec 2010 02:10:27 +0100
parents df4ebc8add66
children 4541ca17975b
line wrap: on
line source


// written in the D programming language

module chipmunkd.cpArbiter;

import chipmunkd.chipmunk;
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 CP_MAX_CONTACTS_PER_ARBITER = 6;

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* a;
	cpShape* 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.b, (*b) = arb.a;
	} else {
		(*a) = arb.a, (*b) = arb.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 int
cpArbiterGetCount(const cpArbiter *arb)
{
	return arb.numContacts;
}

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;
}


static cpFloat 
cpArbiteGetDepth(const cpArbiter *arb, int i)
{
	return arb.contacts[i].dist;
}

struct cpContactPointSet {
	int count;
	
	struct TPoint
	{
		cpVect point, normal;
		cpFloat dist = 0;
	}
	
	TPoint[CP_MAX_CONTACTS_PER_ARBITER] points;
}

static cpContactPointSet
cpArbiterGetContactPointSet(const cpArbiter *arb)
{
	cpContactPointSet set;
	set.count = cpArbiterGetCount(arb);
	
	int i;
	for(i=0; i<set.count; i++){
		set.points[i].point = arb.contacts[i].p;
		set.points[i].normal = arb.contacts[i].p;
		set.points[i].dist = arb.contacts[i].dist;
	}
	
	return set;
}

// cpArbiter.c --------------------------

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.handler = null;
	arb.swappedColl = cpFalse;
	   
	arb.e = 0.0f;
	arb.u = 0.0f;
	arb.surface_vr = cpvzero;
	
	arb.numContacts = 0;
	arb.contacts = null;
	
	arb.a = a;
	arb.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;
				}
			}
		}
	}
	
	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.a = a;
	arb.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.a._body;
	cpBody *b = arb.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.a;
	cpShape *shapeb = arb.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.a._body;
	cpBody *b = arb.b._body;

	for(int i=0; i<arb.numContacts; i++){
		cpContact *con = &arb.contacts[i];
		cpVect n = con.n;
		cpVect r1 = con.r1;
		cpVect r2 = con.r2;
		
		// Calculate the relative bias velocities.
		cpVect vb1 = cpvadd(a.v_bias, cpvmult(cpvperp(r1), a.w_bias));
		cpVect vb2 = cpvadd(b.v_bias, cpvmult(cpvperp(r2), b.w_bias));
		cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n);
		
		// Calculate and clamp the bias impulse.
		cpFloat jbn = (con.bias - vbn)*con.nMass;
		cpFloat jbnOld = con.jBias;
		con.jBias = cpfmax(jbnOld + jbn, 0.0f);
		jbn = con.jBias - jbnOld;
		
		// Apply the bias impulse.
		apply_bias_impulses(a, b, r1, r2, cpvmult(n, jbn));

		// Calculate the relative velocity.
		cpVect vr = relative_velocity(a, b, r1, r2);
		cpFloat vrn = cpvdot(vr, n);
		
		// Calculate and clamp the normal impulse.
		cpFloat jn = -(con.bounce*eCoef + vrn)*con.nMass;
		cpFloat jnOld = con.jnAcc;
		con.jnAcc = cpfmax(jnOld + jn, 0.0f);
		jn = con.jnAcc - jnOld;
		
		// Calculate the relative tangent velocity.
		cpFloat vrt = cpvdot(cpvadd(vr, arb.surface_vr), cpvperp(n));
		
		// Calculate and clamp the friction impulse.
		cpFloat jtMax = arb.u*con.jnAcc;
		cpFloat jt = -vrt*con.tMass;
		cpFloat jtOld = con.jtAcc;
		con.jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax);
		jt = con.jtAcc - jtOld;
		
		// Apply the final impulse.
		apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(jn, jt)));
	}
}