0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-12-26 13:49:01 -05:00

added broadphase to Matter.Detector

This commit is contained in:
liabru 2021-12-12 11:25:57 +00:00
parent b9e7d9dd8b
commit a6b5e7d849
2 changed files with 193 additions and 110 deletions

View file

@ -1,58 +1,106 @@
/** /**
* The `Matter.Detector` module contains methods for detecting collisions given a set of pairs. * The `Matter.Detector` module contains methods for efficiently detecting collisions between a list of bodies using a broadphase algorithm.
* *
* @class Detector * @class Detector
*/ */
// TODO: speculative contacts
var Detector = {}; var Detector = {};
module.exports = Detector; module.exports = Detector;
var SAT = require('./SAT'); var Common = require('../core/Common');
var Pair = require('./Pair'); var Collision = require('./Collision');
(function() { (function() {
/** /**
* Finds all collisions given a list of pairs. * Creates a new collision detector.
* @method collisions * @method create
* @param {pair[]} broadphasePairs * @param {} options
* @param {engine} engine * @return {detector} A new collision detector
* @return {array} collisions
*/ */
Detector.collisions = function(broadphasePairs, engine) { Detector.create = function(options) {
var defaults = {
bodies: [],
pairs: null
};
return Common.extend(defaults, options);
};
/**
* Sets the list of bodies in the detector.
* @method setBodies
* @param {detector} detector
* @param {body[]} bodies
*/
Detector.setBodies = function(detector, bodies) {
detector.bodies = bodies.slice(0);
};
/**
* Clears the detector including its list of bodies.
* @method clear
* @param {detector} detector
*/
Detector.clear = function(detector) {
detector.bodies = [];
};
/**
* Efficiently finds all collisions among all the bodies in `detector.bodies` using a broadphase algorithm.
*
* _Note:_ The specific ordering of collisions returned is not guaranteed between releases and may change for performance reasons.
* If a specific ordering is required then apply a sort to the resulting array.
* @method collisions
* @param {detector} detector
* @return {collision[]} collisions
*/
Detector.collisions = function(detector) {
var collisions = [], var collisions = [],
pairs = engine.pairs, pairs = detector.pairs,
broadphasePairsLength = broadphasePairs.length, bodies = detector.bodies,
bodiesLength = bodies.length,
canCollide = Detector.canCollide, canCollide = Detector.canCollide,
collides = SAT.collides, collides = Collision.collides,
i; i,
j;
for (i = 0; i < broadphasePairsLength; i++) { bodies.sort(Detector._compareBoundsX);
var broadphasePair = broadphasePairs[i],
bodyA = broadphasePair[0],
bodyB = broadphasePair[1];
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping)) for (i = 0; i < bodiesLength; i++) {
continue; var bodyA = bodies[i],
boundsA = bodyA.bounds,
boundXMax = bodyA.bounds.max.x,
boundYMax = bodyA.bounds.max.y,
boundYMin = bodyA.bounds.min.y,
bodyAStatic = bodyA.isStatic || bodyA.isSleeping,
partsALength = bodyA.parts.length,
partsASingle = partsALength === 1;
if (!canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) for (j = i + 1; j < bodiesLength; j++) {
continue; var bodyB = bodies[j],
var boundsA = bodyA.bounds,
boundsB = bodyB.bounds; boundsB = bodyB.bounds;
if (boundsA.min.x > boundsB.max.x || boundsA.max.x < boundsB.min.x if (boundsB.min.x > boundXMax) {
|| boundsA.max.y < boundsB.min.y || boundsA.min.y > boundsB.max.y) { break;
}
if (boundYMax < boundsB.min.y || boundYMin > boundsB.max.y) {
continue; continue;
} }
var partsALength = bodyA.parts.length, if (bodyAStatic && (bodyB.isStatic || bodyB.isSleeping)) {
partsBLength = bodyB.parts.length; continue;
}
if (partsALength === 1 && partsBLength === 1) { if (!canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) {
continue;
}
var partsBLength = bodyB.parts.length;
if (partsASingle && partsBLength === 1) {
var collision = collides(bodyA, bodyB, pairs); var collision = collides(bodyA, bodyB, pairs);
if (collision) { if (collision) {
@ -62,12 +110,12 @@ var Pair = require('./Pair');
var partsAStart = partsALength > 1 ? 1 : 0, var partsAStart = partsALength > 1 ? 1 : 0,
partsBStart = partsBLength > 1 ? 1 : 0; partsBStart = partsBLength > 1 ? 1 : 0;
for (var j = partsAStart; j < partsALength; j++) { for (var k = partsAStart; k < partsALength; k++) {
var partA = bodyA.parts[j], var partA = bodyA.parts[k],
boundsA = partA.bounds; boundsA = partA.bounds;
for (var k = partsBStart; k < partsBLength; k++) { for (var z = partsBStart; z < partsBLength; z++) {
var partB = bodyB.parts[k], var partB = bodyB.parts[z],
boundsB = partB.bounds; boundsB = partB.bounds;
if (boundsA.min.x > boundsB.max.x || boundsA.max.x < boundsB.min.x if (boundsA.min.x > boundsB.max.x || boundsA.max.x < boundsB.min.x
@ -84,6 +132,7 @@ var Pair = require('./Pair');
} }
} }
} }
}
return collisions; return collisions;
}; };
@ -103,4 +152,39 @@ var Pair = require('./Pair');
return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0; return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0;
}; };
/**
* The comparison function used in the broadphase algorithm.
* Returns the signed delta of the bodies bounds on the x-axis.
* @private
* @method _sortCompare
* @param {body} bodyA
* @param {body} bodyB
* @return {number} The signed delta used for sorting
*/
Detector._compareBoundsX = function(bodyA, bodyB) {
return bodyA.bounds.min.x - bodyB.bounds.min.x;
};
/*
*
* Properties Documentation
*
*/
/**
* The array of `Matter.Body` between which the detector finds collisions.
*
* _Note:_ The order of bodies in this array _is not fixed_ and will be continually managed by the detector.
* @property bodies
* @type body[]
* @default []
*/
/**
* Optional. A `Matter.Pairs` object from which previous collision objects may be reused. Intended for internal `Matter.Engine` usage.
* @property pairs
* @type {pairs|null}
* @default null
*/
})(); })();

View file

@ -16,7 +16,6 @@ var Sleeping = require('./Sleeping');
var Resolver = require('../collision/Resolver'); var Resolver = require('../collision/Resolver');
var Detector = require('../collision/Detector'); var Detector = require('../collision/Detector');
var Pairs = require('../collision/Pairs'); var Pairs = require('../collision/Pairs');
var Grid = require('../collision/Grid');
var Events = require('./Events'); var Events = require('./Events');
var Composite = require('../body/Composite'); var Composite = require('../body/Composite');
var Constraint = require('../constraint/Constraint'); var Constraint = require('../constraint/Constraint');
@ -43,7 +42,6 @@ var Body = require('../body/Body');
enableSleeping: false, enableSleeping: false,
events: [], events: [],
plugin: {}, plugin: {},
grid: null,
gravity: { gravity: {
x: 0, x: 0,
y: 1, y: 1,
@ -60,10 +58,11 @@ var Body = require('../body/Body');
var engine = Common.extend(defaults, options); var engine = Common.extend(defaults, options);
engine.world = options.world || Composite.create({ label: 'World' }); engine.world = options.world || Composite.create({ label: 'World' });
engine.grid = Grid.create(options.grid || options.broadphase); engine.pairs = options.pairs || Pairs.create();
engine.pairs = Pairs.create(); engine.detector = options.detector || Detector.create();
// temporary back compatibility // for temporary back compatibility only
engine.grid = { buckets: [] };
engine.world.gravity = engine.gravity; engine.world.gravity = engine.gravity;
engine.broadphase = engine.grid; engine.broadphase = engine.grid;
engine.metrics = {}; engine.metrics = {};
@ -93,9 +92,10 @@ var Body = require('../body/Body');
correction = correction || 1; correction = correction || 1;
var world = engine.world, var world = engine.world,
detector = engine.detector,
pairs = engine.pairs,
timing = engine.timing, timing = engine.timing,
grid = engine.grid, timestamp = timing.timestamp,
gridPairs = [],
i; i;
// increment timestamp // increment timestamp
@ -109,15 +109,25 @@ var Body = require('../body/Body');
Events.trigger(engine, 'beforeUpdate', event); Events.trigger(engine, 'beforeUpdate', event);
// get lists of all bodies and constraints, no matter what composites they are in // get all bodies and all constraints in the world
var allBodies = Composite.allBodies(world), var allBodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world); allConstraints = Composite.allConstraints(world);
// if sleeping enabled, call the sleeping controller // update the detector bodies if they have changed
if (world.isModified) {
Detector.setBodies(detector, allBodies);
}
// reset all composite modified flags
if (world.isModified) {
Composite.setModified(world, false, false, true);
}
// update sleeping if enabled
if (engine.enableSleeping) if (engine.enableSleeping)
Sleeping.update(allBodies, timing.timeScale); Sleeping.update(allBodies, timing.timeScale);
// applies gravity to all bodies // apply gravity to all bodies
Engine._bodiesApplyGravity(allBodies, engine.gravity); Engine._bodiesApplyGravity(allBodies, engine.gravity);
// update all body position and rotation by integration // update all body position and rotation by integration
@ -130,27 +140,11 @@ var Body = require('../body/Body');
} }
Constraint.postSolveAll(allBodies); Constraint.postSolveAll(allBodies);
// broadphase pass: find potential collision pairs // find all collisions
detector.pairs = engine.pairs;
// if world is dirty, we must flush the whole grid var collisions = Detector.collisions(detector);
if (world.isModified)
Grid.clear(grid);
// update the grid buckets based on current bodies
Grid.update(grid, allBodies, engine, world.isModified);
gridPairs = grid.pairsList;
// clear all composite modified flags
if (world.isModified) {
Composite.setModified(world, false, false, true);
}
// narrowphase pass: find actual collisions, then create or update collision pairs
var collisions = Detector.collisions(gridPairs, engine);
// update collision pairs // update collision pairs
var pairs = engine.pairs,
timestamp = timing.timestamp;
Pairs.update(pairs, collisions, timestamp); Pairs.update(pairs, collisions, timestamp);
// wake up bodies involved in collisions // wake up bodies involved in collisions
@ -224,17 +218,13 @@ var Body = require('../body/Body');
}; };
/** /**
* Clears the engine including the world, pairs and broadphase. * Clears the engine pairs and detector.
* @method clear * @method clear
* @param {engine} engine * @param {engine} engine
*/ */
Engine.clear = function(engine) { Engine.clear = function(engine) {
var world = engine.world,
bodies = Composite.allBodies(world);
Pairs.clear(engine.pairs); Pairs.clear(engine.pairs);
Grid.clear(engine.grid); Detector.clear(engine.detector);
Grid.update(engine.grid, bodies, engine, true);
}; };
/** /**
@ -314,53 +304,53 @@ var Body = require('../body/Body');
* Fired just before an update * Fired just before an update
* *
* @event beforeUpdate * @event beforeUpdate
* @param {} event An event object * @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update and all collision events * Fired after engine update and all collision events
* *
* @event afterUpdate * @event afterUpdate
* @param {} event An event object * @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any) * Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any)
* *
* @event collisionStart * @event collisionStart
* @param {} event An event object * @param {object} event An event object
* @param {} event.pairs List of affected pairs * @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any) * Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any)
* *
* @event collisionActive * @event collisionActive
* @param {} event An event object * @param {object} event An event object
* @param {} event.pairs List of affected pairs * @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any) * Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any)
* *
* @event collisionEnd * @event collisionEnd
* @param {} event An event object * @param {object} event An event object
* @param {} event.pairs List of affected pairs * @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/* /*
@ -452,9 +442,18 @@ var Body = require('../body/Body');
* @default 0 * @default 0
*/ */
/**
* A `Matter.Detector` instance.
*
* @property detector
* @type detector
* @default a Matter.Detector instance
*/
/** /**
* A `Matter.Grid` instance. * A `Matter.Grid` instance.
* *
* @deprecated replaced by `engine.detector`
* @property grid * @property grid
* @type grid * @type grid
* @default a Matter.Grid instance * @default a Matter.Grid instance
@ -463,7 +462,7 @@ var Body = require('../body/Body');
/** /**
* Replaced by and now alias for `engine.grid`. * Replaced by and now alias for `engine.grid`.
* *
* @deprecated use `engine.grid` * @deprecated replaced by `engine.detector`
* @property broadphase * @property broadphase
* @type grid * @type grid
* @default a Matter.Grid instance * @default a Matter.Grid instance