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:
parent
2b5eb92556
commit
0add2f00c9
6 changed files with 106 additions and 52 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
})();
|
|
@ -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');
|
||||
|
|
Loading…
Reference in a new issue