0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-11-30 10:20:52 -05:00

More fine-grained collision filtering in the style of Box2D

This commit is contained in:
Gustav Carlson 2014-07-22 14:44:09 +02:00
parent 6e1ab9a24c
commit dd4fc65cf2
5 changed files with 139 additions and 34 deletions

View file

@ -61,6 +61,7 @@
<option value="beachBalls">Beach Balls</option> <option value="beachBalls">Beach Balls</option>
<option value="stress">Stress 1</option> <option value="stress">Stress 1</option>
<option value="stress2">Stress 2</option> <option value="stress2">Stress 2</option>
<option value="collisionFiltering">Collision Filtering</option>
</select> </select>
<input id="demo-reset" value="Reset" type="submit"> <input id="demo-reset" value="Reset" type="submit">
</div> </div>

View file

@ -448,12 +448,12 @@
Demo.chains = function() { Demo.chains = function() {
var _world = _engine.world, var _world = _engine.world,
groupId = Body.nextGroupId(); groupId = Body.nextNonCollidingGroupId();
Demo.reset(); Demo.reset();
var ropeA = Composites.stack(200, 100, 5, 2, 10, 10, function(x, y, column, row) { var ropeA = Composites.stack(200, 100, 5, 2, 10, 10, function(x, y, column, row) {
return Bodies.rectangle(x, y, 50, 20, { groupId: groupId }); return Bodies.rectangle(x, y, 50, 20, { collisionFilter: {group: groupId} });
}); });
Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 }); Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
@ -466,10 +466,10 @@
World.add(_world, ropeA); World.add(_world, ropeA);
groupId = Body.nextGroupId(); groupId = Body.nextNonCollidingGroupId();
var ropeB = Composites.stack(500, 100, 5, 2, 10, 10, function(x, y, column, row) { var ropeB = Composites.stack(500, 100, 5, 2, 10, 10, function(x, y, column, row) {
return Bodies.circle(x, y, 20, { groupId: groupId }); return Bodies.circle(x, y, 20, { collisionFilter: {group: groupId} });
}); });
Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 }); Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
@ -485,12 +485,12 @@
Demo.bridge = function() { Demo.bridge = function() {
var _world = _engine.world, var _world = _engine.world,
groupId = Body.nextGroupId(); groupId = Body.nextNonCollidingGroupId();
Demo.reset(); Demo.reset();
var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y, column, row) { var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y, column, row) {
return Bodies.rectangle(x, y, 50, 20, { groupId: groupId }); return Bodies.rectangle(x, y, 50, 20, { collisionFilter: {group: groupId} });
}); });
Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 }); Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 });
@ -937,8 +937,8 @@
Demo.reset(); Demo.reset();
var groupId = Body.nextGroupId(), var groupId = Body.nextNonCollidingGroupId(),
particleOptions = { friction: 0.00001, groupId: groupId, render: { visible: false }}, particleOptions = { friction: 0.00001, collisionFilter: { group: groupId }, render: { visible: false }},
cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions); cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions);
for (var i = 0; i < 20; i++) { for (var i = 0; i < 20; i++) {
@ -1231,6 +1231,44 @@
var renderOptions = _engine.render.options; var renderOptions = _engine.render.options;
}; };
Demo.collisionFiltering = function() {
var _world = _engine.world;
Demo.reset();
var i;
World.add(_world,
Composites.stack(275, 150, 5, 10, 10, 10, function(x, y, column, row) {
return Bodies.circle(x, y, 20, {
collisionFilter: {
category: row < 7 ? 2 : 4
},
render: {
strokeStyle: row < 7 ? 'red' : 'green',
fillStyle: 'transparent'
}
});
})
);
World.add(_world,
Bodies.circle(400, 40, 30, {
collisionFilter: {
mask: 5
},
render: {
fillStyle: 'blue'
}
})
);
var renderOptions = _engine.render.options;
renderOptions.wireframes = false;
renderOptions.background = '#111';
renderOptions.showCollisions = true;
};
// the functions for the demo interface and controls below // the functions for the demo interface and controls below
Demo.initControls = function() { Demo.initControls = function() {

View file

@ -15,7 +15,8 @@ var Body = {};
Body._inertiaScale = 4; Body._inertiaScale = 4;
var _nextGroupId = 1; var _nextCollidingGroupId = 1,
_nextNonCollidingGroupId = -1;
/** /**
* Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults. * Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults.
@ -49,7 +50,11 @@ var Body = {};
restitution: 0, restitution: 0,
friction: 0.1, friction: 0.1,
frictionAir: 0.01, frictionAir: 0.01,
groupId: 0, collisionFilter: {
category: 1,
mask: 0xFFFFFFFF,
group: 0
},
slop: 0.05, slop: 0.05,
timeScale: 1, timeScale: 1,
render: { render: {
@ -70,12 +75,21 @@ var Body = {};
}; };
/** /**
* Returns the next unique groupID number. * Returns the next unique groupID number for which bodies will collide.
* @method nextGroupId * @method nextCollidingGroupId
* @return {Number} Unique groupID * @return {Number} Unique groupID
*/ */
Body.nextGroupId = function() { Body.nextCollidingGroupId = function() {
return _nextGroupId++; return _nextCollidingGroupId++;
};
/**
* Returns the next collisionFilter.group value for which bodies will not collide.
* @method nextNonCollidingGroupId
* @return {Number} Unique groupID
*/
Body.nextNonCollidingGroupId = function() {
return _nextNonCollidingGroupId--;
}; };
/** /**
@ -673,18 +687,56 @@ var Body = {};
*/ */
/** /**
* An integer `Number` that specifies the collision group the body belongs to. * An `Object` that specifies the collision filtering properties of this body.
* Bodies with the same `groupId` are considered _as-one_ body and therefore do not interact.
* This allows for creation of segmented bodies that can self-intersect, such as a rope.
* The default value 0 means the body does not belong to a group, and can interact with all other bodies.
* *
* @property groupId * Collisions between two bodies will obey the following rules:
* @type number * - If the two bodies have the same non-zero value of `collisionFilter.group`,
* they will always collide if the value is positive, and they will never collide
* if the value is negative.
* - If the two bodies have different values of `collisionFilter.group` or if one
* (or both) of the bodies has a value of 0, then the category/mask rules apply as follows:
*
* Each body belongs to a collision category, given by `collisionFilter.category`. This
* value is used as a bit field and the category should have only one bit set, meaning that
* the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32
* different collision categories available.
* Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies
* the categories it collides with (the value is the bitwise AND value of all these categories).
*
* Using the category/mask rules, two bodies `A` and `B` collide if each includes the other's
* category in its mask, i.e. `(categoryA & maskB) !== 0` and `(categoryB & maskA) !== 0`
* are both true.
*
* @property collisionFilter
* @type object
*/
/**
* An Integer `Number`, see `collisionFilter`
*
* @property collisionFilter.group
* @type object
* @default 0 * @default 0
*/ */
/** /**
* A `Number` that specifies a tollerance on how far a body is allowed to 'sink' or rotate into other bodies. * An Integer `Number`, see `collisionFilter`
*
* @property collisionFilter.category
* @type object
* @default 1
*/
/**
* An Integer `Number`, see `collisionFilter`
*
* @property collisionFilter.mask
* @type object
* @default -1
*/
/**
* A `Number` that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies.
* Avoid changing this value unless you understand the purpose of `slop` in physics engines. * Avoid changing this value unless you understand the purpose of `slop` in physics engines.
* The default should generally suffice, although very large bodies may require larger values for stable stacking. * The default should generally suffice, although very large bodies may require larger values for stable stacking.
* *

View file

@ -26,14 +26,15 @@ var Detector = {};
var bodyA = broadphasePairs[i][0], var bodyA = broadphasePairs[i][0],
bodyB = broadphasePairs[i][1]; bodyB = broadphasePairs[i][1];
// NOTE: could share a function for the below, but may drop performance? var collisionFilterA = bodyA.collisionFilter,
collisionFilterB = bodyB.collisionFilter;
if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId)
continue;
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping)) if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
continue; continue;
if (!_pairCollides(collisionFilterA, collisionFilterB))
continue;
metrics.midphaseTests += 1; metrics.midphaseTests += 1;
// mid phase // mid phase
@ -128,4 +129,11 @@ var Detector = {};
return collisions; return collisions;
}; };
var _pairCollides = function(filterA, filterB) {
if (filterA.group === filterB.group && filterA.group !== 0)
return filterA.group > 0;
return ((filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0);
};
})(); })();

View file

@ -226,7 +226,7 @@ var Composites = {};
* @return {composite} A new composite car body * @return {composite} A new composite car body
*/ */
Composites.car = function(xx, yy, width, height, wheelSize) { Composites.car = function(xx, yy, width, height, wheelSize) {
var groupId = Body.nextGroupId(), var collisionFilterGroup = Body.nextNonCollidingGroupId(),
wheelBase = -20, wheelBase = -20,
wheelAOffset = -width * 0.5 + wheelBase, wheelAOffset = -width * 0.5 + wheelBase,
wheelBOffset = width * 0.5 - wheelBase, wheelBOffset = width * 0.5 - wheelBase,
@ -234,7 +234,9 @@ var Composites = {};
var car = Composite.create({ label: 'Car' }), var car = Composite.create({ label: 'Car' }),
body = Bodies.trapezoid(xx, yy, width, height, 0.3, { body = Bodies.trapezoid(xx, yy, width, height, 0.3, {
groupId: groupId, collisionFilter: {
group: collisionFilterGroup
},
friction: 0.01, friction: 0.01,
chamfer: { chamfer: {
radius: 10 radius: 10
@ -242,14 +244,18 @@ var Composites = {};
}); });
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, { var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
groupId: groupId, collisionFilter: {
group: collisionFilterGroup
},
restitution: 0.5, restitution: 0.5,
friction: 0.9, friction: 0.9,
density: 0.01 density: 0.01
}); });
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, { var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
groupId: groupId, collisionFilter: {
group: collisionFilterGroup
},
restitution: 0.5, restitution: 0.5,
friction: 0.9, friction: 0.9,
density: 0.01 density: 0.01