Mercurial > projects > chipmunkd
diff trunk/chipmunkd/cpSpaceComponent.d @ 4:7ebbd4d05553
initial commit
author | Extrawurst |
---|---|
date | Thu, 02 Dec 2010 02:11:26 +0100 |
parents | |
children | df4ebc8add66 |
line wrap: on
line diff
--- /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); +}