0
0
Fork 0
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:
liabru 2021-12-08 23:38:13 +00:00
parent 9037f36f31
commit fd1a70ec7a
5 changed files with 104 additions and 148 deletions

View file

@ -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);
}
}

View file

@ -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++) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
};
})();