Skip to content

Commit

Permalink
Merge branch 'collision-filters' of https://github.com/GustavCarlson/…
Browse files Browse the repository at this point in the history
…matter-js into GustavCarlson-collision-filters
  • Loading branch information
liabru committed Jul 29, 2014
2 parents 04955bb + dd4fc65 commit 3a32bda
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 34 deletions.
3 changes: 2 additions & 1 deletion demo/dev.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ <h1>Matter.js Demo (Dev. Build)</h1>
<option value="beachBalls">Beach Balls</option>
<option value="stress">Stress 1</option>
<option value="stress2">Stress 2</option>
<option value="collisionFiltering">Collision Filtering</option>
</select>
<input id="demo-reset" value="Reset" type="submit">
</div>
<div id="canvas-container"></div>
</div>
</body>
</html>
</html>
56 changes: 47 additions & 9 deletions demo/js/Demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,12 +448,12 @@

Demo.chains = function() {
var _world = _engine.world,
groupId = Body.nextGroupId();
groupId = Body.nextNonCollidingGroupId();

Demo.reset();

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 });
Expand All @@ -466,10 +466,10 @@

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) {
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 });
Expand All @@ -485,12 +485,12 @@

Demo.bridge = function() {
var _world = _engine.world,
groupId = Body.nextGroupId();
groupId = Body.nextNonCollidingGroupId();

Demo.reset();

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 });
Expand Down Expand Up @@ -937,8 +937,8 @@

Demo.reset();

var groupId = Body.nextGroupId(),
particleOptions = { friction: 0.00001, groupId: groupId, render: { visible: false }},
var groupId = Body.nextNonCollidingGroupId(),
particleOptions = { friction: 0.00001, collisionFilter: { group: groupId }, render: { visible: false }},
cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions);

for (var i = 0; i < 20; i++) {
Expand Down Expand Up @@ -1231,6 +1231,44 @@
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

Demo.initControls = function() {
Expand Down Expand Up @@ -1400,4 +1438,4 @@
renderOptions.showDebug = true;
};

})();
})();
80 changes: 66 additions & 14 deletions src/body/Body.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var Body = {};

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.
Expand Down Expand Up @@ -49,7 +50,11 @@ var Body = {};
restitution: 0,
friction: 0.1,
frictionAir: 0.01,
groupId: 0,
collisionFilter: {
category: 1,
mask: 0xFFFFFFFF,
group: 0
},
slop: 0.05,
timeScale: 1,
render: {
Expand All @@ -70,12 +75,21 @@ var Body = {};
};

/**
* Returns the next unique groupID number.
* @method nextGroupId
* Returns the next unique groupID number for which bodies will collide.
* @method nextCollidingGroupId
* @return {Number} Unique groupID
*/
Body.nextGroupId = function() {
return _nextGroupId++;
Body.nextCollidingGroupId = function() {
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--;
};

/**
Expand Down Expand Up @@ -672,18 +686,56 @@ var Body = {};
*/

/**
* An integer `Number` that specifies the collision group the body belongs to.
* 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.
* An `Object` that specifies the collision filtering properties of this body.
*
* @property groupId
* @type number
* Collisions between two bodies will obey the following rules:
* - 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
*/

/**
* 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.
* The default should generally suffice, although very large bodies may require larger values for stable stacking.
*
Expand Down Expand Up @@ -797,4 +849,4 @@ var Body = {};
* @type bounds
*/

})();
})();
18 changes: 13 additions & 5 deletions src/collision/Detector.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ var Detector = {};
var bodyA = broadphasePairs[i][0],
bodyB = broadphasePairs[i][1];

// NOTE: could share a function for the below, but may drop performance?

if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId)
continue;
var collisionFilterA = bodyA.collisionFilter,
collisionFilterB = bodyB.collisionFilter;

if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
continue;

if (!_pairCollides(collisionFilterA, collisionFilterB))
continue;

metrics.midphaseTests += 1;

Expand Down Expand Up @@ -127,5 +128,12 @@ var Detector = {};

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

})();
})();
16 changes: 11 additions & 5 deletions src/factory/Composites.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,30 +226,36 @@ var Composites = {};
* @return {composite} A new composite car body
*/
Composites.car = function(xx, yy, width, height, wheelSize) {
var groupId = Body.nextGroupId(),
var collisionFilterGroup = Body.nextNonCollidingGroupId(),
wheelBase = -20,
wheelAOffset = -width * 0.5 + wheelBase,
wheelBOffset = width * 0.5 - wheelBase,
wheelYOffset = 0;

var car = Composite.create({ label: 'Car' }),
body = Bodies.trapezoid(xx, yy, width, height, 0.3, {
groupId: groupId,
collisionFilter: {
group: collisionFilterGroup
},
friction: 0.01,
chamfer: {
radius: 10
}
});

var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
groupId: groupId,
collisionFilter: {
group: collisionFilterGroup
},
restitution: 0.5,
friction: 0.9,
density: 0.01
});

var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
groupId: groupId,
collisionFilter: {
group: collisionFilterGroup
},
restitution: 0.5,
friction: 0.9,
density: 0.01
Expand Down Expand Up @@ -308,4 +314,4 @@ var Composites = {};
return softBody;
};

})();
})();

0 comments on commit 3a32bda

Please sign in to comment.