0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2025-01-11 16:00:48 -05:00

optimised SAT.collides

This commit is contained in:
liabru 2021-05-01 21:56:22 +01:00
parent 2096961846
commit 0af144c78b
2 changed files with 89 additions and 63 deletions

View file

@ -55,11 +55,9 @@ var Bounds = require('../geometry/Bounds');
var partB = bodyB.parts[k]; var partB = bodyB.parts[k];
if ((partA === bodyA && partB === bodyB) || overlaps(partA.bounds, partB.bounds)) { if ((partA === bodyA && partB === bodyB) || overlaps(partA.bounds, partB.bounds)) {
// find a previous collision we could reuse
var pair = pairsTable[pairId(partA, partB)];
// narrow phase // narrow phase
var collision = collides(partA, partB, pair && pair.isActive ? pair.collision : null); var pair = pairsTable[pairId(partA, partB)];
var collision = collides(partA, partB, pair && pair.collision, pair && pair.isActive);
if (collision.collided) { if (collision.collided) {
collisions.push(collision); collisions.push(collision);

View file

@ -27,20 +27,47 @@ var Vector = require('../geometry/Vector');
axisNumber: 0 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: []
};
};
/** /**
* Detect collision between two bodies using the Separating Axis Theorem. * Detect collision between two bodies using the Separating Axis Theorem.
* @method collides * @method collides
* @param {body} bodyA * @param {body} bodyA
* @param {body} bodyB * @param {body} bodyB
* @param {collision} previousCollision * @param {?collision} previousCollision
* @param {?boolean} [previousPairActive=false]
* @return {collision} collision * @return {collision} collision
*/ */
SAT.collides = function(bodyA, bodyB, previousCollision) { SAT.collides = function(bodyA, bodyB, previousCollision, pairActive) {
var minOverlap, var minOverlap,
collision, collision = previousCollision || SAT.create(bodyA, bodyB),
canReusePrevCol = false; canReusePrevCol;
if (previousCollision) { if (pairActive && previousCollision.collided) {
// estimate total motion // estimate total motion
var parentA = bodyA.parent, var parentA = bodyA.parent,
parentB = bodyB.parent, parentB = bodyB.parent,
@ -49,24 +76,18 @@ var Vector = require('../geometry/Vector');
// we may be able to (partially) reuse collision result // we may be able to (partially) reuse collision result
// but only safe if collision was resting // but only safe if collision was resting
canReusePrevCol = previousCollision && previousCollision.collided && motion < 0.2; canReusePrevCol = motion < 0.2;
// reuse collision object
collision = previousCollision;
} else {
collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
} }
if (previousCollision && canReusePrevCol) { if (canReusePrevCol) {
// if we can reuse the collision result // if we can reuse the collision result
// we only need to test the previously found axis // we only need to test the previously found axis
var axisBodyA = collision.axisBody, var axisBodyA = previousCollision.axisBodyA,
axisBodyB = axisBodyA === bodyA ? bodyB : bodyA, axisBodyB = previousCollision.axisBodyB,
axes = [axisBodyA.axes[previousCollision.axisNumber]]; axes = [axisBodyA.axes[previousCollision.axisNumber]];
SAT._overlapAxes(_overlapAB, axisBodyA.vertices, axisBodyB.vertices, axes); SAT._overlapAxes(_overlapAB, axisBodyA.vertices, axisBodyB.vertices, axes);
collision.reused = true;
if (_overlapAB.overlap <= 0) { if (_overlapAB.overlap <= 0) {
collision.collided = false; collision.collided = false;
return collision; return collision;
@ -92,10 +113,12 @@ var Vector = require('../geometry/Vector');
if (_overlapAB.overlap < _overlapBA.overlap) { if (_overlapAB.overlap < _overlapBA.overlap) {
minOverlap = _overlapAB; minOverlap = _overlapAB;
collision.axisBody = bodyA; collision.axisBodyA = bodyA;
collision.axisBodyB = bodyB;
} else { } else {
minOverlap = _overlapBA; minOverlap = _overlapBA;
collision.axisBody = bodyB; collision.axisBodyA = bodyB;
collision.axisBodyB = bodyA;
} }
// important for reuse later // important for reuse later
@ -112,52 +135,51 @@ var Vector = require('../geometry/Vector');
bodyA = collision.bodyA; bodyA = collision.bodyA;
bodyB = collision.bodyB; bodyB = collision.bodyB;
var normal = collision.normal,
supports = collision.supports;
// ensure normal is facing away from bodyA // ensure normal is facing away from bodyA
if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) { if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) {
collision.normal = { normal.x = minOverlap.axis.x;
x: minOverlap.axis.x, normal.y = minOverlap.axis.y;
y: minOverlap.axis.y
};
} else { } else {
collision.normal = { normal.x = -minOverlap.axis.x;
x: -minOverlap.axis.x, normal.y = -minOverlap.axis.y;
y: -minOverlap.axis.y
};
} }
collision.tangent = Vector.perp(collision.normal); collision.tangent.x = -normal.y;
collision.tangent.y = normal.x;
collision.penetration = collision.penetration || {}; collision.penetration.x = normal.x * collision.depth;
collision.penetration.x = collision.normal.x * collision.depth; collision.penetration.y = normal.y * collision.depth;
collision.penetration.y = collision.normal.y * collision.depth;
// find support points, there is always either exactly one or two // find support points, there is always either exactly one or two
var verticesB = SAT._findSupports(bodyA, bodyB, collision.normal, 1), var supportsB = SAT._findSupports(bodyA, bodyB, collision.normal, 1);
supports = [];
// clear supports
supports.length = 0;
// find the supports from bodyB that are inside bodyA // find the supports from bodyB that are inside bodyA
if (Vertices.contains(bodyA.vertices, verticesB[0])) if (Vertices.contains(bodyA.vertices, supportsB[0]))
supports.push(verticesB[0]); supports.push(supportsB[0]);
if (Vertices.contains(bodyA.vertices, verticesB[1])) if (Vertices.contains(bodyA.vertices, supportsB[1]))
supports.push(verticesB[1]); supports.push(supportsB[1]);
// find the supports from bodyA that are inside bodyB // find the supports from bodyA that are inside bodyB
if (supports.length < 2) { if (supports.length < 2) {
var verticesA = SAT._findSupports(bodyB, bodyA, collision.normal, -1); var supportsA = SAT._findSupports(bodyB, bodyA, collision.normal, -1);
if (Vertices.contains(bodyB.vertices, verticesA[0])) if (Vertices.contains(bodyB.vertices, supportsA[0]))
supports.push(verticesA[0]); supports.push(supportsA[0]);
if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1])) if (supports.length < 2 && Vertices.contains(bodyB.vertices, supportsA[1]))
supports.push(verticesA[1]); supports.push(supportsA[1]);
} }
// account for the edge case of overlapping but no vertex containment // account for the edge case of overlapping but no vertex containment
if (supports.length < 1) if (supports.length === 0)
supports = [verticesB[0]]; supports.push(supportsB[0]);
collision.supports = supports;
return collision; return collision;
}; };
@ -174,22 +196,26 @@ var Vector = require('../geometry/Vector');
SAT._overlapAxes = function(result, verticesA, verticesB, axes) { SAT._overlapAxes = function(result, verticesA, verticesB, axes) {
var verticesALength = verticesA.length, var verticesALength = verticesA.length,
verticesBLength = verticesB.length, verticesBLength = verticesB.length,
verticesAX = verticesA[0].x,
verticesAY = verticesA[0].y,
verticesBX = verticesB[0].x,
verticesBY = verticesB[0].y,
axesLength = axes.length, axesLength = axes.length,
dot, overlapMin = Number.MAX_VALUE,
overlapAxisNumber = 0,
overlap, overlap,
overlapAB, overlapAB,
overlapBA, overlapBA,
dot,
i, i,
j; j;
result.overlap = Number.MAX_VALUE;
for (i = 0; i < axesLength; i++) { for (i = 0; i < axesLength; i++) {
var axis = axes[i], var axis = axes[i],
axisX = axis.x, axisX = axis.x,
axisY = axis.y, axisY = axis.y,
minA = verticesA[0].x * axisX + verticesA[0].y * axisY, minA = verticesAX * axisX + verticesAY * axisY,
minB = verticesB[0].x * axisX + verticesB[0].y * axisY, minB = verticesBX * axisX + verticesBY * axisY,
maxA = minA, maxA = minA,
maxB = minB; maxB = minB;
@ -217,18 +243,20 @@ var Vector = require('../geometry/Vector');
overlapBA = maxB - minA; overlapBA = maxB - minA;
overlap = overlapAB < overlapBA ? overlapAB : overlapBA; overlap = overlapAB < overlapBA ? overlapAB : overlapBA;
if (overlap <= 0) { if (overlap < overlapMin) {
result.overlap = overlap; overlapMin = overlap;
result.axisNumber = i; overlapAxisNumber = i;
return;
}
if (overlap < result.overlap) { if (overlap <= 0) {
result.overlap = overlap; // can not be intersecting
result.axis = axis; break;
result.axisNumber = i; }
} }
} }
result.axis = axes[overlapAxisNumber];
result.axisNumber = overlapAxisNumber;
result.overlap = overlapMin;
}; };
/** /**