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:
parent
2b5eb92556
commit
0add2f00c9
6 changed files with 106 additions and 52 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,30 +15,60 @@ 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;
|
||||||
|
|
||||||
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)
|
// can reuse if collision was resting
|
||||||
return collision;
|
canReusePrevCol = prevCol && prevCol.collided && motion < 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
overlapBA = _overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes);
|
if (prevCol && canReusePrevCol) {
|
||||||
|
|
||||||
if (overlapBA.overlap === 0)
|
// only need to test the previously found axis
|
||||||
return collision;
|
var axes = [prevCol.bodyA.axes[prevCol.axisNumber]];
|
||||||
|
minOverlap = _overlapAxes(prevCol.bodyA.vertices, prevCol.bodyB.vertices, axes);
|
||||||
|
|
||||||
if (overlapAB.overlap < overlapBA.overlap) {
|
collision = previousCollision;
|
||||||
minOverlap = overlapAB;
|
collision.reused = true;
|
||||||
|
|
||||||
|
if (minOverlap.overlap <= 0) {
|
||||||
|
collision.collided = false;
|
||||||
|
return collision;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
minOverlap = overlapBA;
|
collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
|
||||||
collision.bodyA = bodyB;
|
|
||||||
collision.bodyB = bodyA;
|
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;
|
collision.collided = true;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
|
@ -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');
|
||||||
|
|
Loading…
Reference in a new issue