0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-12-25 13:39:06 -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
* @method collisions
* @param {pair[]} pairs
* @param {metrics} metrics
* @param {pair[]} broadphasePairs
* @param {engine} engine
* @return {array} collisions
*/
Detector.collisions = function(pairs, metrics) {
var collisions = [];
Detector.collisions = function(broadphasePairs, engine) {
var collisions = [],
metrics = engine.metrics,
pairsTable = engine.pairs.table;
for (var i = 0; i < pairs.length; i++) {
var bodyA = pairs[i][0],
bodyB = pairs[i][1];
for (var i = 0; i < broadphasePairs.length; i++) {
var bodyA = broadphasePairs[i][0],
bodyB = broadphasePairs[i][1];
// NOTE: could share a function for the below, but may drop performance?
@ -36,12 +38,26 @@ var Detector = {};
// mid phase
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
var collision = SAT.collides(bodyA, bodyB);
var collision = SAT.collides(bodyA, bodyB, previousCollision);
metrics.narrowphaseTests += 1;
if (collision.reused)
metrics.narrowReuseCount += 1;
if (collision.collided) {
collisions.push(collision);
metrics.narrowDetections += 1;
@ -56,11 +72,13 @@ var Detector = {};
* Description
* @method bruteForce
* @param {body[]} bodies
* @param {metrics} metrics
* @param {engine} engine
* @return {array} collisions
*/
Detector.bruteForce = function(bodies, metrics) {
var collisions = [];
Detector.bruteForce = function(bodies, engine) {
var collisions = [],
metrics = engine.metrics,
pairsTable = engine.pairs.table;
for (var i = 0; i < bodies.length; i++) {
for (var j = i + 1; j < bodies.length; j++) {
@ -80,11 +98,25 @@ var Detector = {};
// mid phase
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
var collision = SAT.collides(bodyA, bodyB);
var collision = SAT.collides(bodyA, bodyB, previousCollision);
metrics.narrowphaseTests += 1;
if (collision.reused)
metrics.narrowReuseCount += 1;
if (collision.collided) {
collisions.push(collision);
metrics.narrowDetections += 1;

View file

@ -220,7 +220,7 @@ var Grid = {};
// keep track of the number of buckets the pair exists in
// important for Grid.update to work
var pairId = _getPairId(body, bodyB);
var pairId = Pair.id(body, bodyB);
if (grid.pairs[pairId]) {
grid.pairs[pairId][2] += 1;
} else {
@ -252,7 +252,7 @@ var Grid = {};
} else {
// keep track of the number of buckets the pair exists in
// important for Grid.update to work
var pairId = _getPairId(body, bodyB);
var pairId = Pair.id(body, bodyB);
if (grid.pairs[pairId]) {
var pairCount = grid.pairs[pairId][2];
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
* @method _createActivePairsList

View file

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

View file

@ -262,9 +262,9 @@ var Engine = {};
} else {
broadphasePairs = world.bodies;
}
// 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
var pairs = engine.pairs,

View file

@ -17,6 +17,8 @@ var Metrics = {};
return {
narrowDetections: 0,
narrowphaseTests: 0,
narrowReuse: 0,
narrowReuseCount: 0,
midphaseTests: 0,
broadphaseTests: 0,
narrowEff: 0.0001,
@ -37,6 +39,8 @@ var Metrics = {};
Metrics.reset = function(metrics) {
metrics.narrowDetections = 0;
metrics.narrowphaseTests = 0;
metrics.narrowReuse = 0;
metrics.narrowReuseCount = 0;
metrics.midphaseTests = 0;
metrics.broadphaseTests = 0;
metrics.narrowEff = 0;
@ -64,8 +68,9 @@ var Metrics = {};
metrics.midEff = (metrics.narrowDetections / (metrics.midphaseTests || 1)).toFixed(2);
metrics.narrowEff = (metrics.narrowDetections / (metrics.narrowphaseTests || 1)).toFixed(2);
metrics.broadEff = (1 - (metrics.broadphaseTests / (world.bodies.length || 1))).toFixed(2);
if (broadphase.instance)
metrics.buckets = Common.keys(broadphase.instance.buckets).length;
metrics.narrowReuse = (metrics.narrowReuseCount / (metrics.narrowphaseTests || 1)).toFixed(2);
//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, 'midEff').listen();
metrics.add(engine.metrics, 'narrowEff').listen();
metrics.add(engine.metrics, 'narrowReuse').listen();
metrics.close();
var controls = datGui.addFolder('Add Body');