0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-12-27 13:59:01 -05:00

implemented collision caching, big performance boost

This commit is contained in:
liabru 2014-03-15 17:28:51 +00:00
parent 2b5eb92556
commit 0add2f00c9
6 changed files with 106 additions and 52 deletions

View file

@ -13,16 +13,18 @@ var Detector = {};
/** /**
* Description * Description
* @method collisions * @method collisions
* @param {pair[]} pairs * @param {pair[]} broadphasePairs
* @param {metrics} metrics * @param {engine} engine
* @return {array} collisions * @return {array} collisions
*/ */
Detector.collisions = function(pairs, metrics) { Detector.collisions = function(broadphasePairs, engine) {
var collisions = []; var collisions = [],
metrics = engine.metrics,
pairsTable = engine.pairs.table;
for (var i = 0; i < pairs.length; i++) { for (var i = 0; i < broadphasePairs.length; i++) {
var bodyA = pairs[i][0], var bodyA = broadphasePairs[i][0],
bodyB = pairs[i][1]; bodyB = broadphasePairs[i][1];
// NOTE: could share a function for the below, but may drop performance? // NOTE: could share a function for the below, but may drop performance?
@ -37,11 +39,25 @@ var Detector = {};
// mid phase // mid phase
if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) { if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) {
// find a previous collision we could reuse
var pairId = Pair.id(bodyA, bodyB),
pair = pairId in pairsTable ? pairsTable[pairId] : null,
previousCollision;
if (pair && pair.isActive) {
previousCollision = pair.collision;
} else {
previousCollision = null;
}
// narrow phase // narrow phase
var collision = SAT.collides(bodyA, bodyB); var collision = SAT.collides(bodyA, bodyB, previousCollision);
metrics.narrowphaseTests += 1; metrics.narrowphaseTests += 1;
if (collision.reused)
metrics.narrowReuseCount += 1;
if (collision.collided) { if (collision.collided) {
collisions.push(collision); collisions.push(collision);
metrics.narrowDetections += 1; metrics.narrowDetections += 1;
@ -56,11 +72,13 @@ var Detector = {};
* Description * Description
* @method bruteForce * @method bruteForce
* @param {body[]} bodies * @param {body[]} bodies
* @param {metrics} metrics * @param {engine} engine
* @return {array} collisions * @return {array} collisions
*/ */
Detector.bruteForce = function(bodies, metrics) { Detector.bruteForce = function(bodies, engine) {
var collisions = []; var collisions = [],
metrics = engine.metrics,
pairsTable = engine.pairs.table;
for (var i = 0; i < bodies.length; i++) { for (var i = 0; i < bodies.length; i++) {
for (var j = i + 1; j < bodies.length; j++) { for (var j = i + 1; j < bodies.length; j++) {
@ -80,11 +98,25 @@ var Detector = {};
// mid phase // mid phase
if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) { if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) {
// find a previous collision we could reuse
var pairId = Pair.id(bodyA, bodyB),
pair = pairId in pairsTable ? pairsTable[pairId] : null,
previousCollision;
if (pair && pair.isActive) {
previousCollision = pair.collision;
} else {
previousCollision = null;
}
// narrow phase // narrow phase
var collision = SAT.collides(bodyA, bodyB); var collision = SAT.collides(bodyA, bodyB, previousCollision);
metrics.narrowphaseTests += 1; metrics.narrowphaseTests += 1;
if (collision.reused)
metrics.narrowReuseCount += 1;
if (collision.collided) { if (collision.collided) {
collisions.push(collision); collisions.push(collision);
metrics.narrowDetections += 1; metrics.narrowDetections += 1;

View file

@ -220,7 +220,7 @@ var Grid = {};
// keep track of the number of buckets the pair exists in // keep track of the number of buckets the pair exists in
// important for Grid.update to work // important for Grid.update to work
var pairId = _getPairId(body, bodyB); var pairId = Pair.id(body, bodyB);
if (grid.pairs[pairId]) { if (grid.pairs[pairId]) {
grid.pairs[pairId][2] += 1; grid.pairs[pairId][2] += 1;
} else { } else {
@ -252,7 +252,7 @@ var Grid = {};
} else { } else {
// keep track of the number of buckets the pair exists in // keep track of the number of buckets the pair exists in
// important for Grid.update to work // important for Grid.update to work
var pairId = _getPairId(body, bodyB); var pairId = Pair.id(body, bodyB);
if (grid.pairs[pairId]) { if (grid.pairs[pairId]) {
var pairCount = grid.pairs[pairId][2]; var pairCount = grid.pairs[pairId][2];
grid.pairs[pairId][2] = pairCount > 0 ? pairCount - 1 : 0; grid.pairs[pairId][2] = pairCount > 0 ? pairCount - 1 : 0;
@ -261,21 +261,6 @@ var Grid = {};
} }
}; };
/**
* Description
* @method _getPairId
* @private
* @param {} bodyA
* @param {} bodyB
*/
var _getPairId = function(bodyA, bodyB) {
if (bodyA.id < bodyB.id) {
return bodyA.id + ',' + bodyB.id;
} else {
return bodyB.id + ',' + bodyA.id;
}
};
/** /**
* Description * Description
* @method _createActivePairsList * @method _createActivePairsList

View file

@ -4,9 +4,7 @@
* @class SAT * @class SAT
*/ */
// TODO: true circles and curves // TODO: true circles and curves
// TODO: cache the previously found axis and body, and start there first for faster early out
var SAT = {}; var SAT = {};
@ -17,22 +15,49 @@ var SAT = {};
* @method collides * @method collides
* @param {body} bodyA * @param {body} bodyA
* @param {body} bodyB * @param {body} bodyB
* @param {collision} previousCollision
* @return {collision} collision * @return {collision} collision
*/ */
SAT.collides = function(bodyA, bodyB) { SAT.collides = function(bodyA, bodyB, previousCollision) {
var overlapAB, var overlapAB,
overlapBA, overlapBA,
minOverlap, minOverlap,
collision = { collided: false, bodyA: bodyA, bodyB: bodyB}; collision,
prevCol = previousCollision,
canReusePrevCol = false;
if (prevCol) {
var motion = bodyA.speed * bodyA.speed + bodyA.angularSpeed * bodyA.angularSpeed;
motion += bodyB.speed * bodyB.speed + bodyB.angularSpeed * bodyB.angularSpeed;
// can reuse if collision was resting
canReusePrevCol = prevCol && prevCol.collided && motion < 0.2;
}
if (prevCol && canReusePrevCol) {
// only need to test the previously found axis
var axes = [prevCol.bodyA.axes[prevCol.axisNumber]];
minOverlap = _overlapAxes(prevCol.bodyA.vertices, prevCol.bodyB.vertices, axes);
collision = previousCollision;
collision.reused = true;
if (minOverlap.overlap <= 0) {
collision.collided = false;
return collision;
}
} else {
collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
overlapAB = _overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes); overlapAB = _overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes);
if (overlapAB.overlap === 0) if (overlapAB.overlap <= 0)
return collision; return collision;
overlapBA = _overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes); overlapBA = _overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes);
if (overlapBA.overlap === 0) if (overlapBA.overlap <= 0)
return collision; return collision;
if (overlapAB.overlap < overlapBA.overlap) { if (overlapAB.overlap < overlapBA.overlap) {
@ -43,6 +68,9 @@ var SAT = {};
collision.bodyB = bodyA; collision.bodyB = bodyA;
} }
collision.axisNumber = minOverlap.axisNumber;
}
collision.collided = true; collision.collided = true;
collision.normal = minOverlap.axis; collision.normal = minOverlap.axis;
collision.depth = minOverlap.overlap; collision.depth = minOverlap.overlap;
@ -111,12 +139,15 @@ var SAT = {};
? projectionA.max - projectionB.min ? projectionA.max - projectionB.min
: projectionB.max - projectionA.min; : projectionB.max - projectionA.min;
if (overlap <= 0) if (overlap <= 0) {
return { overlap: 0 }; result.overlap = overlap;
return result;
}
if (overlap < result.overlap) { if (overlap < result.overlap) {
result.overlap = overlap; result.overlap = overlap;
result.axis = axis; result.axis = axis;
result.axisNumber = i;
} }
} }

View file

@ -264,7 +264,7 @@ var Engine = {};
} }
// narrowphase pass: find actual collisions, then create or update collision pairs // narrowphase pass: find actual collisions, then create or update collision pairs
var collisions = broadphase.detector(broadphasePairs, engine.metrics); var collisions = broadphase.detector(broadphasePairs, engine);
// update pairs // update pairs
var pairs = engine.pairs, var pairs = engine.pairs,

View file

@ -17,6 +17,8 @@ var Metrics = {};
return { return {
narrowDetections: 0, narrowDetections: 0,
narrowphaseTests: 0, narrowphaseTests: 0,
narrowReuse: 0,
narrowReuseCount: 0,
midphaseTests: 0, midphaseTests: 0,
broadphaseTests: 0, broadphaseTests: 0,
narrowEff: 0.0001, narrowEff: 0.0001,
@ -37,6 +39,8 @@ var Metrics = {};
Metrics.reset = function(metrics) { Metrics.reset = function(metrics) {
metrics.narrowDetections = 0; metrics.narrowDetections = 0;
metrics.narrowphaseTests = 0; metrics.narrowphaseTests = 0;
metrics.narrowReuse = 0;
metrics.narrowReuseCount = 0;
metrics.midphaseTests = 0; metrics.midphaseTests = 0;
metrics.broadphaseTests = 0; metrics.broadphaseTests = 0;
metrics.narrowEff = 0; metrics.narrowEff = 0;
@ -64,8 +68,9 @@ var Metrics = {};
metrics.midEff = (metrics.narrowDetections / (metrics.midphaseTests || 1)).toFixed(2); metrics.midEff = (metrics.narrowDetections / (metrics.midphaseTests || 1)).toFixed(2);
metrics.narrowEff = (metrics.narrowDetections / (metrics.narrowphaseTests || 1)).toFixed(2); metrics.narrowEff = (metrics.narrowDetections / (metrics.narrowphaseTests || 1)).toFixed(2);
metrics.broadEff = (1 - (metrics.broadphaseTests / (world.bodies.length || 1))).toFixed(2); metrics.broadEff = (1 - (metrics.broadphaseTests / (world.bodies.length || 1))).toFixed(2);
if (broadphase.instance) metrics.narrowReuse = (metrics.narrowReuseCount / (metrics.narrowphaseTests || 1)).toFixed(2);
metrics.buckets = Common.keys(broadphase.instance.buckets).length; //if (broadphase.instance)
// metrics.buckets = Common.keys(broadphase.instance.buckets).length;
}; };
})(); })();

View file

@ -94,6 +94,7 @@ var Gui = {};
metrics.add(engine.metrics, 'broadEff').listen(); metrics.add(engine.metrics, 'broadEff').listen();
metrics.add(engine.metrics, 'midEff').listen(); metrics.add(engine.metrics, 'midEff').listen();
metrics.add(engine.metrics, 'narrowEff').listen(); metrics.add(engine.metrics, 'narrowEff').listen();
metrics.add(engine.metrics, 'narrowReuse').listen();
metrics.close(); metrics.close();
var controls = datGui.addFolder('Add Body'); var controls = datGui.addFolder('Add Body');