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

refactored pair manager, added new events to engine

This commit is contained in:
liabru 2014-03-10 20:08:11 +00:00
parent 21732978ce
commit 88bd7bc29f
6 changed files with 156 additions and 77 deletions

View file

@ -8,77 +8,112 @@ var Manager = {};
(function() { (function() {
var _pairMaxIdleLife = 500; var _pairMaxIdleLife = 1000;
/** /**
* Description * Description
* @method updatePairs * @method updatePairs
* @param {object} pairs * @param {object} pairs
* @param {pair[]} pairsList * @param {collision[]} collisions
* @param {pair[]} candidatePairs
* @param {metrics} metrics
* @param {detector} detector
* @return {bool} pairsUpdated flag
*/ */
Manager.updatePairs = function(pairs, pairsList, candidatePairs, metrics, detector) { Manager.updatePairs = function(pairs, collisions) {
var i; var pairsList = pairs.list,
pairsTable = pairs.table,
collisionStart = pairs.collisionStart,
collisionEnd = pairs.collisionEnd,
collisionActive = pairs.collisionActive,
activePairIds = [],
collision,
pairId,
pair,
i;
// first set all pairs inactive // clear collision state arrays, but maintain old reference
for (i = 0; i < pairsList.length; i++) { collisionStart.length = 0;
var pair = pairsList[i]; collisionEnd.length = 0;
Pair.setActive(pair, false); collisionActive.length = 0;
}
// detect collisions in current step
var pairsUpdated = false,
collisions = detector(candidatePairs, metrics);
// set collision pairs to active, or create if pair is new
for (i = 0; i < collisions.length; i++) { for (i = 0; i < collisions.length; i++) {
var collision = collisions[i], collision = collisions[i];
if (collision.collided) {
pairId = Pair.id(collision.bodyA, collision.bodyB); pairId = Pair.id(collision.bodyA, collision.bodyB);
activePairIds.push(pairId);
if (pairId in pairs) {
Pair.update(pairs[pairId], collision); if (pairId in pairsTable) {
} else { // pair already exists (but may or may not be active)
pairs[pairId] = Pair.create(collision); pair = pairsTable[pairId];
pairsUpdated = true;
if (pair.isActive) {
// pair exists and is active
collisionActive.push(pair);
} else {
// pair exists but was inactive, so a collision has just started again
collisionStart.push(pair);
}
// update the pair
Pair.update(pair, collision);
} else {
// pair did not exist, create a new pair
pair = Pair.create(collision);
pairsTable[pairId] = pair;
// push the new pair
collisionStart.push(pair);
pairsList.push(pair);
}
}
}
// deactivate previously active pairs that are now inactive
for (i = 0; i < pairsList.length; i++) {
pair = pairsList[i];
if (pair.isActive && activePairIds.indexOf(pair.id) === -1) {
Pair.setActive(pair, false);
collisionEnd.push(pair);
} }
} }
return pairsUpdated;
}; };
/** /**
* Description * Description
* @method removeOldPairs * @method removeOldPairs
* @param {object} pairs * @param {object} pairs
* @param {pair[]} pairsList
* @return {bool} pairsRemoved flag
*/ */
Manager.removeOldPairs = function(pairs, pairsList) { Manager.removeOldPairs = function(pairs) {
var timeNow = Common.now(), var pairsList = pairs.list,
pairsRemoved = false, pairsTable = pairs.table,
timeNow = Common.now(),
indexesToRemove = [],
pair,
collision,
pairIndex,
i; i;
for (i = 0; i < pairsList.length; i++) { for (i = 0; i < pairsList.length; i++) {
var pair = pairsList[i], pair = pairsList[i];
collision = pair.collision; collision = pair.collision;
// never remove sleeping pairs // never remove sleeping pairs
if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) { if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) {
pair.timestamp = timeNow; pair.timeUpdated = timeNow;
continue; continue;
} }
// if pair is inactive for too long, remove it // if pair is inactive for too long, mark it to be removed
if (timeNow - pair.timestamp > _pairMaxIdleLife) { if (timeNow - pair.timeUpdated > _pairMaxIdleLife) {
delete pairs[pair.id]; indexesToRemove.push(i);
pairsRemoved = true;
} }
} }
return pairsRemoved; // remove marked pairs
for (i = 0; i < indexesToRemove.length; i++) {
pairIndex = indexesToRemove[i];
pair = pairsList[pairIndex];
delete pairsTable[pair.id];
pairsList.splice(pairIndex, 1);
}
}; };
})(); })();

View file

@ -16,15 +16,19 @@ var Pair = {};
*/ */
Pair.create = function(collision) { Pair.create = function(collision) {
var bodyA = collision.bodyA, var bodyA = collision.bodyA,
bodyB = collision.bodyB; bodyB = collision.bodyB,
timestamp = Common.now();
var pair = { var pair = {
id: Pair.id(bodyA, bodyB), id: Pair.id(bodyA, bodyB),
bodyA: bodyA,
bodyB: bodyB,
contacts: {}, contacts: {},
activeContacts: [], activeContacts: [],
separation: 0, separation: 0,
isActive: true, isActive: true,
timestamp: Common.now(), timeCreated: timestamp,
timeUpdated: timestamp,
inverseMass: bodyA.inverseMass + bodyB.inverseMass, inverseMass: bodyA.inverseMass + bodyB.inverseMass,
friction: Math.min(bodyA.friction, bodyB.friction), friction: Math.min(bodyA.friction, bodyB.friction),
restitution: Math.max(bodyA.restitution, bodyB.restitution), restitution: Math.max(bodyA.restitution, bodyB.restitution),
@ -78,7 +82,7 @@ var Pair = {};
Pair.setActive = function(pair, isActive) { Pair.setActive = function(pair, isActive) {
if (isActive) { if (isActive) {
pair.isActive = true; pair.isActive = true;
pair.timestamp = Common.now(); pair.timeUpdated = Common.now();
} else { } else {
pair.isActive = false; pair.isActive = false;
pair.activeContacts = []; pair.activeContacts = [];

View file

@ -34,11 +34,17 @@ var Engine = {};
positionIterations: 6, positionIterations: 6,
velocityIterations: 4, velocityIterations: 4,
constraintIterations: 1, constraintIterations: 1,
pairs: {}, pairs: {
pairsList: [], table: {},
list: [],
collisionStart: [],
collisionActive: [],
collisionEnd: []
},
enableSleeping: false, enableSleeping: false,
timeScale: 1, timeScale: 1,
input: {}, input: {},
events: [],
timing: { timing: {
fps: _fps, fps: _fps,
timestamp: 0, timestamp: 0,
@ -78,16 +84,6 @@ var Engine = {};
} }
}; };
engine.events = {
tick: function(engine) {
Engine.update(engine, engine.timing.delta, engine.timing.correction);
},
render: function(engine) {
if (engine.render.options.enabled)
engine.render.controller.world(engine);
}
};
return engine; return engine;
}; };
@ -99,6 +95,7 @@ var Engine = {};
Engine.run = function(engine) { Engine.run = function(engine) {
var timing = engine.timing, var timing = engine.timing,
delta, delta,
correction,
counterTimestamp = 0, counterTimestamp = 0,
frameCounter = 0, frameCounter = 0,
deltaHistory = []; deltaHistory = [];
@ -109,6 +106,13 @@ var Engine = {};
if (!engine.enabled) if (!engine.enabled)
return; return;
// create an event object
var event = {
timestamp: timestamp
};
Events.trigger(engine, 'beforeTick', event);
delta = (timestamp - timing.timestamp) || _delta; delta = (timestamp - timing.timestamp) || _delta;
// optimistically filter delta over a few frames, to improve stability // optimistically filter delta over a few frames, to improve stability
@ -120,20 +124,32 @@ var Engine = {};
delta = delta < engine.timing.deltaMin ? engine.timing.deltaMin : delta; delta = delta < engine.timing.deltaMin ? engine.timing.deltaMin : delta;
delta = delta > engine.timing.deltaMax ? engine.timing.deltaMax : delta; delta = delta > engine.timing.deltaMax ? engine.timing.deltaMax : delta;
// verlet time correction
correction = delta / timing.delta;
// update engine timing object
timing.timestamp = timestamp; timing.timestamp = timestamp;
timing.correction = delta / timing.delta; timing.correction = correction;
timing.delta = delta; timing.delta = delta;
// fps counter
frameCounter += 1; frameCounter += 1;
if (timestamp - counterTimestamp >= 1000) { if (timestamp - counterTimestamp >= 1000) {
timing.fps = frameCounter * ((timestamp - counterTimestamp) / 1000); timing.fps = frameCounter * ((timestamp - counterTimestamp) / 1000);
counterTimestamp = timestamp; counterTimestamp = timestamp;
frameCounter = 0; frameCounter = 0;
} }
engine.events.tick(engine); Events.trigger(engine, 'tick beforeUpdate', event);
engine.events.render(engine);
Engine.update(engine, delta, correction);
Events.trigger(engine, 'afterUpdate beforeRender', event);
if (engine.render.options.enabled)
engine.render.controller.world(engine);
Events.trigger(engine, 'afterTick afterRender', event);
})(); })();
}; };
@ -151,16 +167,17 @@ var Engine = {};
broadphasePairs = [], broadphasePairs = [],
i; i;
Body.resetForcesAll(world.bodies, world.gravity);
Metrics.reset(engine.metrics); Metrics.reset(engine.metrics);
MouseConstraint.update(engine.mouseConstraint, world.bodies, engine.input); MouseConstraint.update(engine.mouseConstraint, world.bodies, engine.input);
Body.updateAll(world.bodies, delta * engine.timeScale, correction, world.bounds); Body.updateAll(world.bodies, delta * engine.timeScale, correction, world.bounds);
// update all constraints
for (i = 0; i < engine.constraintIterations; i++) { for (i = 0; i < engine.constraintIterations; i++) {
Constraint.updateAll(world.constraints); Constraint.updateAll(world.constraints);
} }
// broadphase pass: find potential collision pairs
if (broadphase.controller) { if (broadphase.controller) {
broadphase.controller.update(broadphase.instance, world.bodies, engine); broadphase.controller.update(broadphase.instance, world.bodies, engine);
broadphasePairs = broadphase.instance.pairsList; broadphasePairs = broadphase.instance.pairsList;
@ -168,25 +185,44 @@ var Engine = {};
broadphasePairs = world.bodies; broadphasePairs = world.bodies;
} }
var pairsUpdated = Manager.updatePairs(engine.pairs, engine.pairsList, broadphasePairs, engine.metrics, broadphase.detector), // narrowphase pass: find actual collisions, then create or update collision pairs
pairsRemoved = Manager.removeOldPairs(engine.pairs, engine.pairsList); var collisions = broadphase.detector(broadphasePairs, engine.metrics);
if (pairsUpdated || pairsRemoved) // update pairs
engine.pairsList = Common.values(engine.pairs); var pairs = engine.pairs;
Manager.updatePairs(pairs, collisions);
Manager.removeOldPairs(pairs);
// trigger collision events
if (pairs.collisionStart.length > 0) {
Events.trigger(engine, 'collisionStart', {
pairs: pairs.collisionStart
});
}
if (pairs.collisionActive.length > 0) {
Events.trigger(engine, 'collisionActive', {
pairs: pairs.collisionActive
});
}
if (pairs.collisionEnd.length > 0) {
Events.trigger(engine, 'collisionEnd', {
pairs: pairs.collisionEnd
});
}
// wake up bodies involved in collisions // wake up bodies involved in collisions
if (engine.enableSleeping) if (engine.enableSleeping)
Sleeping.afterCollisions(engine.pairsList); Sleeping.afterCollisions(pairs.list);
// iteratively resolve velocity between collisions // iteratively resolve velocity between collisions
Resolver.preSolveVelocity(engine.pairsList); Resolver.preSolveVelocity(pairs.list);
for (i = 0; i < engine.velocityIterations; i++) { for (i = 0; i < engine.velocityIterations; i++) {
Resolver.solveVelocity(engine.pairsList); Resolver.solveVelocity(pairs.list);
} }
// iteratively resolve position between collisions // iteratively resolve position between collisions
for (i = 0; i < engine.positionIterations; i++) { for (i = 0; i < engine.positionIterations; i++) {
Resolver.solvePosition(engine.pairsList); Resolver.solvePosition(pairs.list);
} }
Resolver.postSolvePosition(world.bodies); Resolver.postSolvePosition(world.bodies);
@ -195,6 +231,9 @@ var Engine = {};
Metrics.update(engine.metrics, engine); Metrics.update(engine.metrics, engine);
// clear force buffers
Body.resetForcesAll(world.bodies, world.gravity);
return engine; return engine;
}; };
@ -238,8 +277,8 @@ var Engine = {};
Engine.clear = function(engine) { Engine.clear = function(engine) {
var world = engine.world; var world = engine.world;
engine.pairs = {}; engine.pairs.table = {};
engine.pairsList = []; engine.pairs.list = [];
World.addComposite(engine.world, engine.mouseConstraint); World.addComposite(engine.world, engine.mouseConstraint);

View file

@ -59,7 +59,7 @@ var Metrics = {};
broadphase = engine.broadphase[engine.broadphase.current]; broadphase = engine.broadphase[engine.broadphase.current];
metrics.collisions = metrics.narrowDetections; metrics.collisions = metrics.narrowDetections;
metrics.pairs = engine.pairsList.length; metrics.pairs = engine.pairs.list.length;
metrics.bodies = world.bodies.length; metrics.bodies = world.bodies.length;
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);

View file

@ -31,6 +31,7 @@ Matter.Vector = Vector;
Matter.Vertices = Vertices; Matter.Vertices = Vertices;
Matter.Gui = Gui; Matter.Gui = Gui;
Matter.Render = Render; Matter.Render = Render;
Matter.Events = Events;
// CommonJS module // CommonJS module
if (typeof exports !== 'undefined') { if (typeof exports !== 'undefined') {

View file

@ -82,8 +82,8 @@ var Render = {};
Render.constraint(world.constraints[i], context); Render.constraint(world.constraints[i], context);
if (options.showCollisions) if (options.showCollisions)
for (i = 0; i < engine.pairsList.length; i++) for (i = 0; i < engine.pairs.list.length; i++)
Render.collision(engine, engine.pairsList[i], context); Render.collision(engine, engine.pairs.list[i], context);
if (options.showBroadphase && engine.broadphase.current === 'grid') if (options.showBroadphase && engine.broadphase.current === 'grid')
Render.grid(engine, engine.broadphase[engine.broadphase.current].instance, context); Render.grid(engine, engine.broadphase[engine.broadphase.current].instance, context);
@ -118,7 +118,7 @@ var Render = {};
text += "\n"; text += "\n";
text += "collisions: " + engine.metrics.collisions + space; text += "collisions: " + engine.metrics.collisions + space;
text += "pairs: " + engine.pairs.length + space; text += "pairs: " + engine.pairs.list.length + space;
text += "broad: " + engine.metrics.broadEff + space; text += "broad: " + engine.metrics.broadEff + space;
text += "mid: " + engine.metrics.midEff + space; text += "mid: " + engine.metrics.midEff + space;
text += "narrow: " + engine.metrics.narrowEff + space; text += "narrow: " + engine.metrics.narrowEff + space;