Skip to content

Commit 3a32bda

Browse files
committed
Merge branch 'collision-filters' of https://github.com/GustavCarlson/matter-js into GustavCarlson-collision-filters
2 parents 04955bb + dd4fc65 commit 3a32bda

5 files changed

Lines changed: 139 additions & 34 deletions

File tree

demo/dev.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ <h1>Matter.js Demo (Dev. Build)</h1>
6161
<option value="beachBalls">Beach Balls</option>
6262
<option value="stress">Stress 1</option>
6363
<option value="stress2">Stress 2</option>
64+
<option value="collisionFiltering">Collision Filtering</option>
6465
</select>
6566
<input id="demo-reset" value="Reset" type="submit">
6667
</div>
6768
<div id="canvas-container"></div>
6869
</div>
6970
</body>
70-
</html>
71+
</html>

demo/js/Demo.js

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,12 @@
448448

449449
Demo.chains = function() {
450450
var _world = _engine.world,
451-
groupId = Body.nextGroupId();
451+
groupId = Body.nextNonCollidingGroupId();
452452

453453
Demo.reset();
454454

455455
var ropeA = Composites.stack(200, 100, 5, 2, 10, 10, function(x, y, column, row) {
456-
return Bodies.rectangle(x, y, 50, 20, { groupId: groupId });
456+
return Bodies.rectangle(x, y, 50, 20, { collisionFilter: {group: groupId} });
457457
});
458458

459459
Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
@@ -466,10 +466,10 @@
466466

467467
World.add(_world, ropeA);
468468

469-
groupId = Body.nextGroupId();
469+
groupId = Body.nextNonCollidingGroupId();
470470

471471
var ropeB = Composites.stack(500, 100, 5, 2, 10, 10, function(x, y, column, row) {
472-
return Bodies.circle(x, y, 20, { groupId: groupId });
472+
return Bodies.circle(x, y, 20, { collisionFilter: {group: groupId} });
473473
});
474474

475475
Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
@@ -485,12 +485,12 @@
485485

486486
Demo.bridge = function() {
487487
var _world = _engine.world,
488-
groupId = Body.nextGroupId();
488+
groupId = Body.nextNonCollidingGroupId();
489489

490490
Demo.reset();
491491

492492
var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y, column, row) {
493-
return Bodies.rectangle(x, y, 50, 20, { groupId: groupId });
493+
return Bodies.rectangle(x, y, 50, 20, { collisionFilter: {group: groupId} });
494494
});
495495

496496
Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 });
@@ -937,8 +937,8 @@
937937

938938
Demo.reset();
939939

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

944944
for (var i = 0; i < 20; i++) {
@@ -1231,6 +1231,44 @@
12311231
var renderOptions = _engine.render.options;
12321232
};
12331233

1234+
Demo.collisionFiltering = function() {
1235+
var _world = _engine.world;
1236+
1237+
Demo.reset();
1238+
1239+
var i;
1240+
1241+
World.add(_world,
1242+
Composites.stack(275, 150, 5, 10, 10, 10, function(x, y, column, row) {
1243+
return Bodies.circle(x, y, 20, {
1244+
collisionFilter: {
1245+
category: row < 7 ? 2 : 4
1246+
},
1247+
render: {
1248+
strokeStyle: row < 7 ? 'red' : 'green',
1249+
fillStyle: 'transparent'
1250+
}
1251+
});
1252+
})
1253+
);
1254+
1255+
World.add(_world,
1256+
Bodies.circle(400, 40, 30, {
1257+
collisionFilter: {
1258+
mask: 5
1259+
},
1260+
render: {
1261+
fillStyle: 'blue'
1262+
}
1263+
})
1264+
);
1265+
1266+
var renderOptions = _engine.render.options;
1267+
renderOptions.wireframes = false;
1268+
renderOptions.background = '#111';
1269+
renderOptions.showCollisions = true;
1270+
};
1271+
12341272
// the functions for the demo interface and controls below
12351273

12361274
Demo.initControls = function() {
@@ -1400,4 +1438,4 @@
14001438
renderOptions.showDebug = true;
14011439
};
14021440

1403-
})();
1441+
})();

src/body/Body.js

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ var Body = {};
1515

1616
Body._inertiaScale = 4;
1717

18-
var _nextGroupId = 1;
18+
var _nextCollidingGroupId = 1,
19+
_nextNonCollidingGroupId = -1;
1920

2021
/**
2122
* 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 = {};
4950
restitution: 0,
5051
friction: 0.1,
5152
frictionAir: 0.01,
52-
groupId: 0,
53+
collisionFilter: {
54+
category: 1,
55+
mask: 0xFFFFFFFF,
56+
group: 0
57+
},
5358
slop: 0.05,
5459
timeScale: 1,
5560
render: {
@@ -70,12 +75,21 @@ var Body = {};
7075
};
7176

7277
/**
73-
* Returns the next unique groupID number.
74-
* @method nextGroupId
78+
* Returns the next unique groupID number for which bodies will collide.
79+
* @method nextCollidingGroupId
7580
* @return {Number} Unique groupID
7681
*/
77-
Body.nextGroupId = function() {
78-
return _nextGroupId++;
82+
Body.nextCollidingGroupId = function() {
83+
return _nextCollidingGroupId++;
84+
};
85+
86+
/**
87+
* Returns the next collisionFilter.group value for which bodies will not collide.
88+
* @method nextNonCollidingGroupId
89+
* @return {Number} Unique groupID
90+
*/
91+
Body.nextNonCollidingGroupId = function() {
92+
return _nextNonCollidingGroupId--;
7993
};
8094

8195
/**
@@ -672,18 +686,56 @@ var Body = {};
672686
*/
673687

674688
/**
675-
* An integer `Number` that specifies the collision group the body belongs to.
676-
* Bodies with the same `groupId` are considered _as-one_ body and therefore do not interact.
677-
* This allows for creation of segmented bodies that can self-intersect, such as a rope.
678-
* The default value 0 means the body does not belong to a group, and can interact with all other bodies.
689+
* An `Object` that specifies the collision filtering properties of this body.
679690
*
680-
* @property groupId
681-
* @type number
691+
* Collisions between two bodies will obey the following rules:
692+
* - If the two bodies have the same non-zero value of `collisionFilter.group`,
693+
* they will always collide if the value is positive, and they will never collide
694+
* if the value is negative.
695+
* - If the two bodies have different values of `collisionFilter.group` or if one
696+
* (or both) of the bodies has a value of 0, then the category/mask rules apply as follows:
697+
*
698+
* Each body belongs to a collision category, given by `collisionFilter.category`. This
699+
* value is used as a bit field and the category should have only one bit set, meaning that
700+
* the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32
701+
* different collision categories available.
702+
* Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies
703+
* the categories it collides with (the value is the bitwise AND value of all these categories).
704+
*
705+
* Using the category/mask rules, two bodies `A` and `B` collide if each includes the other's
706+
* category in its mask, i.e. `(categoryA & maskB) !== 0` and `(categoryB & maskA) !== 0`
707+
* are both true.
708+
*
709+
* @property collisionFilter
710+
* @type object
711+
*/
712+
713+
/**
714+
* An Integer `Number`, see `collisionFilter`
715+
*
716+
* @property collisionFilter.group
717+
* @type object
682718
* @default 0
683719
*/
684720

685721
/**
686-
* A `Number` that specifies a tollerance on how far a body is allowed to 'sink' or rotate into other bodies.
722+
* An Integer `Number`, see `collisionFilter`
723+
*
724+
* @property collisionFilter.category
725+
* @type object
726+
* @default 1
727+
*/
728+
729+
/**
730+
* An Integer `Number`, see `collisionFilter`
731+
*
732+
* @property collisionFilter.mask
733+
* @type object
734+
* @default -1
735+
*/
736+
737+
/**
738+
* A `Number` that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies.
687739
* Avoid changing this value unless you understand the purpose of `slop` in physics engines.
688740
* The default should generally suffice, although very large bodies may require larger values for stable stacking.
689741
*
@@ -797,4 +849,4 @@ var Body = {};
797849
* @type bounds
798850
*/
799851

800-
})();
852+
})();

src/collision/Detector.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ var Detector = {};
2626
var bodyA = broadphasePairs[i][0],
2727
bodyB = broadphasePairs[i][1];
2828

29-
// NOTE: could share a function for the below, but may drop performance?
30-
31-
if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId)
32-
continue;
29+
var collisionFilterA = bodyA.collisionFilter,
30+
collisionFilterB = bodyB.collisionFilter;
3331

3432
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
3533
continue;
34+
35+
if (!_pairCollides(collisionFilterA, collisionFilterB))
36+
continue;
3637

3738
metrics.midphaseTests += 1;
3839

@@ -127,5 +128,12 @@ var Detector = {};
127128

128129
return collisions;
129130
};
131+
132+
var _pairCollides = function(filterA, filterB) {
133+
if (filterA.group === filterB.group && filterA.group !== 0)
134+
return filterA.group > 0;
135+
136+
return ((filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0);
137+
};
130138

131-
})();
139+
})();

src/factory/Composites.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,30 +226,36 @@ var Composites = {};
226226
* @return {composite} A new composite car body
227227
*/
228228
Composites.car = function(xx, yy, width, height, wheelSize) {
229-
var groupId = Body.nextGroupId(),
229+
var collisionFilterGroup = Body.nextNonCollidingGroupId(),
230230
wheelBase = -20,
231231
wheelAOffset = -width * 0.5 + wheelBase,
232232
wheelBOffset = width * 0.5 - wheelBase,
233233
wheelYOffset = 0;
234234

235235
var car = Composite.create({ label: 'Car' }),
236236
body = Bodies.trapezoid(xx, yy, width, height, 0.3, {
237-
groupId: groupId,
237+
collisionFilter: {
238+
group: collisionFilterGroup
239+
},
238240
friction: 0.01,
239241
chamfer: {
240242
radius: 10
241243
}
242244
});
243245

244246
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
245-
groupId: groupId,
247+
collisionFilter: {
248+
group: collisionFilterGroup
249+
},
246250
restitution: 0.5,
247251
friction: 0.9,
248252
density: 0.01
249253
});
250254

251255
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
252-
groupId: groupId,
256+
collisionFilter: {
257+
group: collisionFilterGroup
258+
},
253259
restitution: 0.5,
254260
friction: 0.9,
255261
density: 0.01
@@ -308,4 +314,4 @@ var Composites = {};
308314
return softBody;
309315
};
310316

311-
})();
317+
})();

0 commit comments

Comments
 (0)