mirror of
https://github.com/liabru/matter-js.git
synced 2025-01-20 17:10:11 -05:00
optimised collisions
This commit is contained in:
parent
9037f36f31
commit
fd1a70ec7a
5 changed files with 104 additions and 148 deletions
|
@ -24,11 +24,10 @@ var Pair = require('./Pair');
|
|||
*/
|
||||
Detector.collisions = function(broadphasePairs, engine) {
|
||||
var collisions = [],
|
||||
pairsTable = engine.pairs.table,
|
||||
pairs = engine.pairs,
|
||||
broadphasePairsLength = broadphasePairs.length,
|
||||
canCollide = Detector.canCollide,
|
||||
collides = SAT.collides,
|
||||
pairId = Pair.id,
|
||||
i;
|
||||
|
||||
for (i = 0; i < broadphasePairsLength; i++) {
|
||||
|
@ -54,10 +53,9 @@ var Pair = require('./Pair');
|
|||
partsBLength = bodyB.parts.length;
|
||||
|
||||
if (partsALength === 1 && partsBLength === 1) {
|
||||
var pair = pairsTable[pairId(bodyA, bodyB)];
|
||||
var collision = collides(bodyA, bodyB, pair && pair.collision, pair && pair.isActive);
|
||||
var collision = collides(bodyA, bodyB, pairs);
|
||||
|
||||
if (collision.collided) {
|
||||
if (collision) {
|
||||
collisions.push(collision);
|
||||
}
|
||||
} else {
|
||||
|
@ -77,10 +75,9 @@ var Pair = require('./Pair');
|
|||
continue;
|
||||
}
|
||||
|
||||
var pair = pairsTable[pairId(partA, partB)];
|
||||
var collision = collides(partA, partB, pair && pair.collision, pair && pair.isActive);
|
||||
var collision = collides(partA, partB, pairs);
|
||||
|
||||
if (collision.collided) {
|
||||
if (collision) {
|
||||
collisions.push(collision);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ var Contact = require('./Contact');
|
|||
id: Pair.id(bodyA, bodyB),
|
||||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
collision: collision,
|
||||
contacts: [],
|
||||
activeContacts: [],
|
||||
separation: 0,
|
||||
|
@ -72,6 +73,7 @@ var Contact = require('./Contact');
|
|||
pair.restitution = parentA.restitution > parentB.restitution ? parentA.restitution : parentB.restitution;
|
||||
pair.slop = parentA.slop > parentB.slop ? parentA.slop : parentB.slop;
|
||||
|
||||
collision.pair = pair;
|
||||
activeContacts.length = 0;
|
||||
|
||||
for (var i = 0; i < supports.length; i++) {
|
||||
|
|
|
@ -46,7 +46,6 @@ var Common = require('../core/Common');
|
|||
collisionActive = pairs.collisionActive,
|
||||
collision,
|
||||
pairIndex,
|
||||
pairId,
|
||||
pair,
|
||||
i;
|
||||
|
||||
|
@ -61,8 +60,7 @@ var Common = require('../core/Common');
|
|||
|
||||
for (i = 0; i < collisionsLength; i++) {
|
||||
collision = collisions[i];
|
||||
pairId = Pair.id(collision.bodyA, collision.bodyB);
|
||||
pair = pairsTable[pairId];
|
||||
pair = collision.pair;
|
||||
|
||||
if (pair) {
|
||||
// pair already exists (but may or may not be active)
|
||||
|
@ -80,7 +78,7 @@ var Common = require('../core/Common');
|
|||
} else {
|
||||
// pair did not exist, create a new pair
|
||||
pair = Pair.create(collision, timestamp);
|
||||
pairsTable[pairId] = pair;
|
||||
pairsTable[pair.id] = pair;
|
||||
|
||||
// push the new pair
|
||||
collisionStart.push(pair);
|
||||
|
|
|
@ -26,19 +26,25 @@ var Vertices = require('../geometry/Vertices');
|
|||
* @return {object[]} Collisions
|
||||
*/
|
||||
Query.collides = function(body, bodies) {
|
||||
var collisions = [];
|
||||
var collisions = [],
|
||||
bodiesLength = bodies.length,
|
||||
bounds = body.bounds,
|
||||
collides = SAT.collides,
|
||||
overlaps = Bounds.overlaps;
|
||||
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var bodyA = bodies[i];
|
||||
for (var i = 0; i < bodiesLength; i++) {
|
||||
var bodyA = bodies[i],
|
||||
partsALength = bodyA.parts.length,
|
||||
partsAStart = partsALength === 1 ? 0 : 1;
|
||||
|
||||
if (Bounds.overlaps(bodyA.bounds, body.bounds)) {
|
||||
for (var j = bodyA.parts.length === 1 ? 0 : 1; j < bodyA.parts.length; j++) {
|
||||
if (overlaps(bodyA.bounds, bounds)) {
|
||||
for (var j = partsAStart; j < partsALength; j++) {
|
||||
var part = bodyA.parts[j];
|
||||
|
||||
if (Bounds.overlaps(part.bounds, body.bounds)) {
|
||||
var collision = SAT.collides(part, body);
|
||||
if (overlaps(part.bounds, bounds)) {
|
||||
var collision = collides(part, body);
|
||||
|
||||
if (collision.collided) {
|
||||
if (collision) {
|
||||
collisions.push(collision);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -4,53 +4,25 @@
|
|||
* @class SAT
|
||||
*/
|
||||
|
||||
// TODO: true circles and curves
|
||||
|
||||
var SAT = {};
|
||||
|
||||
module.exports = SAT;
|
||||
|
||||
var Vertices = require('../geometry/Vertices');
|
||||
var Vector = require('../geometry/Vector');
|
||||
var Pair = require('../collision/Pair');
|
||||
var Collision = require('./Collision');
|
||||
|
||||
(function() {
|
||||
var _supports = [];
|
||||
|
||||
var _overlapAB = {
|
||||
overlap: 0,
|
||||
axis: null,
|
||||
axisNumber: 0
|
||||
axis: null
|
||||
};
|
||||
|
||||
var _overlapBA = {
|
||||
overlap: 0,
|
||||
axis: null,
|
||||
axisNumber: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new collision record.
|
||||
* @private
|
||||
* @method create
|
||||
* @param {body} bodyA
|
||||
* @param {body} bodyB
|
||||
* @return {collision} A new collision
|
||||
*/
|
||||
SAT.create = function(bodyA, bodyB) {
|
||||
return {
|
||||
collided: false,
|
||||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
parentA: bodyA.parent,
|
||||
parentB: bodyB.parent,
|
||||
axisBodyA: bodyA,
|
||||
axisBodyB: bodyB,
|
||||
axisNumber: 0,
|
||||
depth: 0,
|
||||
normal: { x: 0, y: 0 },
|
||||
tangent: { x: 0, y: 0 },
|
||||
penetration: { x: 0, y: 0 },
|
||||
supports: []
|
||||
};
|
||||
axis: null
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -58,128 +30,104 @@ var Vector = require('../geometry/Vector');
|
|||
* @method collides
|
||||
* @param {body} bodyA
|
||||
* @param {body} bodyB
|
||||
* @param {?collision} previousCollision
|
||||
* @param {?boolean} [previousPairActive=false]
|
||||
* @return {collision} collision
|
||||
* @param {pairs} [pairs] Optionally reuse collision objects from existing pairs.
|
||||
* @return {collision|null} A collision object if found, otherwise null
|
||||
*/
|
||||
SAT.collides = function(bodyA, bodyB, previousCollision, pairActive) {
|
||||
var minOverlap,
|
||||
collision = previousCollision || SAT.create(bodyA, bodyB),
|
||||
canReusePrevCol;
|
||||
SAT.collides = function(bodyA, bodyB, pairs) {
|
||||
SAT._overlapAxes(_overlapAB, bodyA.vertices, bodyB.vertices, bodyA.axes);
|
||||
|
||||
if (pairActive && previousCollision.collided) {
|
||||
// estimate total motion
|
||||
var parentA = bodyA.parent,
|
||||
parentB = bodyB.parent,
|
||||
motion = parentA.speed * parentA.speed + parentA.angularSpeed * parentA.angularSpeed
|
||||
+ parentB.speed * parentB.speed + parentB.angularSpeed * parentB.angularSpeed;
|
||||
|
||||
// we may be able to (partially) reuse collision result
|
||||
// but only safe if collision was resting
|
||||
canReusePrevCol = motion < 0.2;
|
||||
if (_overlapAB.overlap <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (canReusePrevCol) {
|
||||
// if we can reuse the collision result
|
||||
// we only need to test the previously found axis
|
||||
var axisBodyA = previousCollision.axisBodyA,
|
||||
axisBodyB = previousCollision.axisBodyB,
|
||||
axes = [axisBodyA.axes[previousCollision.axisNumber]];
|
||||
SAT._overlapAxes(_overlapBA, bodyB.vertices, bodyA.vertices, bodyB.axes);
|
||||
|
||||
SAT._overlapAxes(_overlapAB, axisBodyA.vertices, axisBodyB.vertices, axes);
|
||||
|
||||
if (_overlapAB.overlap <= 0) {
|
||||
collision.collided = false;
|
||||
return collision;
|
||||
}
|
||||
if (_overlapBA.overlap <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
minOverlap = _overlapAB;
|
||||
// reuse collision objects for gc efficiency
|
||||
var pair = pairs && pairs.table[Pair.id(bodyA, bodyB)],
|
||||
collision;
|
||||
|
||||
if (!pair) {
|
||||
collision = Collision.create(bodyA, bodyB);
|
||||
collision.collided = true;
|
||||
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
|
||||
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
|
||||
collision.parentA = collision.bodyA.parent;
|
||||
collision.parentB = collision.bodyB.parent;
|
||||
} else {
|
||||
// if we can't reuse a result, perform a full SAT test
|
||||
|
||||
SAT._overlapAxes(_overlapAB, bodyA.vertices, bodyB.vertices, bodyA.axes);
|
||||
|
||||
if (_overlapAB.overlap <= 0) {
|
||||
collision.collided = false;
|
||||
return collision;
|
||||
}
|
||||
|
||||
SAT._overlapAxes(_overlapBA, bodyB.vertices, bodyA.vertices, bodyB.axes);
|
||||
|
||||
if (_overlapBA.overlap <= 0) {
|
||||
collision.collided = false;
|
||||
return collision;
|
||||
}
|
||||
|
||||
if (_overlapAB.overlap < _overlapBA.overlap) {
|
||||
minOverlap = _overlapAB;
|
||||
collision.axisBodyA = bodyA;
|
||||
collision.axisBodyB = bodyB;
|
||||
} else {
|
||||
minOverlap = _overlapBA;
|
||||
collision.axisBodyA = bodyB;
|
||||
collision.axisBodyB = bodyA;
|
||||
}
|
||||
|
||||
// important for reuse later
|
||||
collision.axisNumber = minOverlap.axisNumber;
|
||||
collision = pair.collision;
|
||||
}
|
||||
|
||||
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
|
||||
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
|
||||
collision.collided = true;
|
||||
collision.depth = minOverlap.overlap;
|
||||
collision.parentA = collision.bodyA.parent;
|
||||
collision.parentB = collision.bodyB.parent;
|
||||
|
||||
bodyA = collision.bodyA;
|
||||
bodyB = collision.bodyB;
|
||||
|
||||
var normal = collision.normal,
|
||||
supports = collision.supports;
|
||||
var minOverlap;
|
||||
|
||||
// ensure normal is facing away from bodyA
|
||||
if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) {
|
||||
normal.x = minOverlap.axis.x;
|
||||
normal.y = minOverlap.axis.y;
|
||||
if (_overlapAB.overlap < _overlapBA.overlap) {
|
||||
minOverlap = _overlapAB;
|
||||
} else {
|
||||
normal.x = -minOverlap.axis.x;
|
||||
normal.y = -minOverlap.axis.y;
|
||||
minOverlap = _overlapBA;
|
||||
}
|
||||
|
||||
var normal = collision.normal,
|
||||
supports = collision.supports,
|
||||
minAxis = minOverlap.axis,
|
||||
minAxisX = minAxis.x,
|
||||
minAxisY = minAxis.y;
|
||||
|
||||
// ensure normal is facing away from bodyA
|
||||
if (minAxisX * (bodyB.position.x - bodyA.position.x) + minAxisY * (bodyB.position.y - bodyA.position.y) < 0) {
|
||||
normal.x = minAxisX;
|
||||
normal.y = minAxisY;
|
||||
} else {
|
||||
normal.x = -minAxisX;
|
||||
normal.y = -minAxisY;
|
||||
}
|
||||
|
||||
collision.tangent.x = -normal.y;
|
||||
collision.tangent.y = normal.x;
|
||||
|
||||
collision.depth = minOverlap.overlap;
|
||||
|
||||
collision.penetration.x = normal.x * collision.depth;
|
||||
collision.penetration.y = normal.y * collision.depth;
|
||||
collision.penetration.y = normal.y * collision.depth;
|
||||
|
||||
// find support points, there is always either exactly one or two
|
||||
var supportsB = SAT._findSupports(bodyA, bodyB, collision.normal, 1);
|
||||
|
||||
// clear supports
|
||||
supports.length = 0;
|
||||
var supportsB = SAT._findSupports(bodyA, bodyB, normal, 1),
|
||||
supportCount = 0;
|
||||
|
||||
// find the supports from bodyB that are inside bodyA
|
||||
if (Vertices.contains(bodyA.vertices, supportsB[0]))
|
||||
supports.push(supportsB[0]);
|
||||
if (Vertices.contains(bodyA.vertices, supportsB[0])) {
|
||||
supports[supportCount++] = supportsB[0];
|
||||
}
|
||||
|
||||
if (Vertices.contains(bodyA.vertices, supportsB[1]))
|
||||
supports.push(supportsB[1]);
|
||||
if (Vertices.contains(bodyA.vertices, supportsB[1])) {
|
||||
supports[supportCount++] = supportsB[1];
|
||||
}
|
||||
|
||||
// find the supports from bodyA that are inside bodyB
|
||||
if (supports.length < 2) {
|
||||
var supportsA = SAT._findSupports(bodyB, bodyA, collision.normal, -1);
|
||||
if (supportCount < 2) {
|
||||
var supportsA = SAT._findSupports(bodyB, bodyA, normal, -1);
|
||||
|
||||
if (Vertices.contains(bodyB.vertices, supportsA[0]))
|
||||
supports.push(supportsA[0]);
|
||||
if (Vertices.contains(bodyB.vertices, supportsA[0])) {
|
||||
supports[supportCount++] = supportsA[0];
|
||||
}
|
||||
|
||||
if (supports.length < 2 && Vertices.contains(bodyB.vertices, supportsA[1]))
|
||||
supports.push(supportsA[1]);
|
||||
if (supportCount < 2 && Vertices.contains(bodyB.vertices, supportsA[1])) {
|
||||
supports[supportCount++] = supportsA[1];
|
||||
}
|
||||
}
|
||||
|
||||
// account for the edge case of overlapping but no vertex containment
|
||||
if (supports.length === 0)
|
||||
supports.push(supportsB[0]);
|
||||
if (supportCount === 0) {
|
||||
supports[supportCount++] = supportsB[0];
|
||||
}
|
||||
|
||||
// update supports array size
|
||||
supports.length = supportCount;
|
||||
|
||||
return collision;
|
||||
};
|
||||
|
@ -255,7 +203,6 @@ var Vector = require('../geometry/Vector');
|
|||
}
|
||||
|
||||
result.axis = axes[overlapAxisNumber];
|
||||
result.axisNumber = overlapAxisNumber;
|
||||
result.overlap = overlapMin;
|
||||
};
|
||||
|
||||
|
@ -268,11 +215,11 @@ var Vector = require('../geometry/Vector');
|
|||
* @param {} axis
|
||||
*/
|
||||
SAT._projectToAxis = function(projection, vertices, axis) {
|
||||
var min = Vector.dot(vertices[0], axis),
|
||||
var min = vertices[0].x * axis.x + vertices[0].y * axis.y,
|
||||
max = min;
|
||||
|
||||
for (var i = 1; i < vertices.length; i += 1) {
|
||||
var dot = Vector.dot(vertices[i], axis);
|
||||
var dot = vertices[i].x * axis.x + vertices[i].y * axis.y;
|
||||
|
||||
if (dot > max) {
|
||||
max = dot;
|
||||
|
@ -328,10 +275,16 @@ var Vector = require('../geometry/Vector');
|
|||
// compare with previous vertex
|
||||
vertexB = vertices[(vertexA.index + 1) % verticesLength];
|
||||
if (normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y) < nearestDistance) {
|
||||
return [vertexA, vertexB];
|
||||
_supports[0] = vertexA;
|
||||
_supports[1] = vertexB;
|
||||
|
||||
return _supports;
|
||||
}
|
||||
|
||||
return [vertexA, vertexC];
|
||||
_supports[0] = vertexA;
|
||||
_supports[1] = vertexC;
|
||||
|
||||
return _supports;
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
Loading…
Add table
Reference in a new issue