mirror of
https://github.com/liabru/matter-js.git
synced 2024-11-30 10:20:52 -05:00
Merge branch 'GustavCarlson-collision-filters'
This commit is contained in:
commit
a58fe2a3c7
6 changed files with 222 additions and 41 deletions
|
@ -49,6 +49,7 @@
|
|||
<option value="softBody">Basic Soft Bodies</option>
|
||||
<option value="cloth">Cloth</option>
|
||||
<option value="events">Events</option>
|
||||
<option value="collisionFiltering">Collision Filtering</option>
|
||||
<option value="chains">Chains</option>
|
||||
<option value="ballPool">Ball Pool</option>
|
||||
<option value="stack">Stack</option>
|
||||
|
|
107
demo/js/Demo.js
107
demo/js/Demo.js
|
@ -448,12 +448,12 @@
|
|||
|
||||
Demo.chains = function() {
|
||||
var _world = _engine.world,
|
||||
groupId = Body.nextGroupId();
|
||||
group = Body.nextGroup(true);
|
||||
|
||||
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: group } });
|
||||
});
|
||||
|
||||
Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
|
||||
|
@ -466,10 +466,10 @@
|
|||
|
||||
World.add(_world, ropeA);
|
||||
|
||||
groupId = Body.nextGroupId();
|
||||
group = Body.nextGroup(true);
|
||||
|
||||
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: group } });
|
||||
});
|
||||
|
||||
Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 });
|
||||
|
@ -485,12 +485,12 @@
|
|||
|
||||
Demo.bridge = function() {
|
||||
var _world = _engine.world,
|
||||
groupId = Body.nextGroupId();
|
||||
group = Body.nextGroup(true);
|
||||
|
||||
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: group } });
|
||||
});
|
||||
|
||||
Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 });
|
||||
|
@ -937,8 +937,8 @@
|
|||
|
||||
Demo.reset();
|
||||
|
||||
var groupId = Body.nextGroupId(),
|
||||
particleOptions = { friction: 0.00001, groupId: groupId, render: { visible: false }},
|
||||
var group = Body.nextGroup(true),
|
||||
particleOptions = { friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }},
|
||||
cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions);
|
||||
|
||||
for (var i = 0; i < 20; i++) {
|
||||
|
@ -1231,6 +1231,97 @@
|
|||
var renderOptions = _engine.render.options;
|
||||
};
|
||||
|
||||
Demo.collisionFiltering = function() {
|
||||
var _world = _engine.world;
|
||||
|
||||
// define our categories (as bit fields, there are up to 32 available)
|
||||
|
||||
var defaultCategory = 0x0001,
|
||||
redCategory = 0x0002,
|
||||
greenCategory = 0x0004,
|
||||
blueCategory = 0x0008;
|
||||
|
||||
var redColor = '#C44D58',
|
||||
blueColor = '#4ECDC4',
|
||||
greenColor = '#C7F464';
|
||||
|
||||
Demo.reset();
|
||||
|
||||
// create a stack with varying body categories (but these bodies can all collide with each other)
|
||||
|
||||
World.add(_world,
|
||||
Composites.stack(275, 150, 5, 10, 10, 10, function(x, y, column, row) {
|
||||
var category = redCategory,
|
||||
color = redColor;
|
||||
|
||||
if (row > 5) {
|
||||
category = blueCategory;
|
||||
color = blueColor;
|
||||
} else if (row > 2) {
|
||||
category = greenCategory;
|
||||
color = greenColor;
|
||||
}
|
||||
|
||||
return Bodies.circle(x, y, 20, {
|
||||
collisionFilter: {
|
||||
category: category
|
||||
},
|
||||
render: {
|
||||
strokeStyle: color,
|
||||
fillStyle: 'transparent'
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
// this body will only collide with the walls and the green bodies
|
||||
|
||||
World.add(_world,
|
||||
Bodies.circle(310, 40, 30, {
|
||||
collisionFilter: {
|
||||
mask: defaultCategory | greenCategory
|
||||
},
|
||||
render: {
|
||||
strokeStyle: Common.shadeColor(greenColor, -20),
|
||||
fillStyle: greenColor
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// this body will only collide with the walls and the red bodies
|
||||
|
||||
World.add(_world,
|
||||
Bodies.circle(400, 40, 30, {
|
||||
collisionFilter: {
|
||||
mask: defaultCategory | redCategory
|
||||
},
|
||||
render: {
|
||||
strokeStyle: Common.shadeColor(redColor, -20),
|
||||
fillStyle: redColor
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// this body will only collide with the walls and the blue bodies
|
||||
|
||||
World.add(_world,
|
||||
Bodies.circle(480, 40, 30, {
|
||||
collisionFilter: {
|
||||
mask: defaultCategory | blueCategory
|
||||
},
|
||||
render: {
|
||||
strokeStyle: Common.shadeColor(blueColor, -20),
|
||||
fillStyle: blueColor
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
var renderOptions = _engine.render.options;
|
||||
renderOptions.wireframes = false;
|
||||
renderOptions.background = '#222';
|
||||
renderOptions.showAngleIndicator = false;
|
||||
};
|
||||
|
||||
// the functions for the demo interface and controls below
|
||||
|
||||
Demo.initControls = function() {
|
||||
|
|
|
@ -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.
|
||||
|
@ -49,7 +50,11 @@ var Body = {};
|
|||
restitution: 0,
|
||||
friction: 0.1,
|
||||
frictionAir: 0.01,
|
||||
groupId: 0,
|
||||
collisionFilter: {
|
||||
category: 0x0001,
|
||||
mask: 0xFFFFFFFF,
|
||||
group: 0
|
||||
},
|
||||
slop: 0.05,
|
||||
timeScale: 1,
|
||||
render: {
|
||||
|
@ -70,12 +75,18 @@ var Body = {};
|
|||
};
|
||||
|
||||
/**
|
||||
* Returns the next unique groupID number.
|
||||
* @method nextGroupId
|
||||
* @return {Number} Unique groupID
|
||||
* Returns the next unique group index for which bodies will collide.
|
||||
* If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide.
|
||||
* See `body.collisionFilter` for more information.
|
||||
* @method nextGroup
|
||||
* @param {bool} [isNonColliding=false]
|
||||
* @return {Number} Unique group index
|
||||
*/
|
||||
Body.nextGroupId = function() {
|
||||
return _nextGroupId++;
|
||||
Body.nextGroup = function(isNonColliding) {
|
||||
if (isNonColliding)
|
||||
return _nextNonCollidingGroupId--;
|
||||
|
||||
return _nextCollidingGroupId++;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -672,18 +683,62 @@ 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`, that specifies the collision group this body belongs to.
|
||||
* See `body.collisionFilter` for more information.
|
||||
*
|
||||
* @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.
|
||||
* A bit field that specifies the collision category this body belongs to.
|
||||
* The category value should have only one bit set, for example `0x0001`.
|
||||
* This means there are up to 32 unique collision categories available.
|
||||
* See `body.collisionFilter` for more information.
|
||||
*
|
||||
* @property collisionFilter.category
|
||||
* @type object
|
||||
* @default 1
|
||||
*/
|
||||
|
||||
/**
|
||||
* A bit mask that specifies the collision categories this body may collide with.
|
||||
* See `body.collisionFilter` for more information.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
|
|
|
@ -26,12 +26,10 @@ 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)
|
||||
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
|
||||
continue;
|
||||
|
||||
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
|
||||
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
|
||||
continue;
|
||||
|
||||
metrics.midphaseTests += 1;
|
||||
|
@ -87,10 +85,10 @@ var Detector = {};
|
|||
|
||||
// NOTE: could share a function for the below, but may drop performance?
|
||||
|
||||
if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId)
|
||||
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
|
||||
continue;
|
||||
|
||||
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
|
||||
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
|
||||
continue;
|
||||
|
||||
metrics.midphaseTests += 1;
|
||||
|
@ -128,4 +126,19 @@ var Detector = {};
|
|||
return collisions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if both supplied collision filters will allow a collision to occur.
|
||||
* See `body.collisionFilter` for more information.
|
||||
* @method canCollide
|
||||
* @param {} filterA
|
||||
* @param {} filterB
|
||||
* @return {bool} `true` if collision can occur
|
||||
*/
|
||||
Detector.canCollide = 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;
|
||||
};
|
||||
|
||||
})();
|
|
@ -42,7 +42,12 @@ var MouseConstraint = {};
|
|||
mouse: mouse,
|
||||
dragBody: null,
|
||||
dragPoint: null,
|
||||
constraint: constraint
|
||||
constraint: constraint,
|
||||
collisionFilter: {
|
||||
category: 0x0001,
|
||||
mask: 0xFFFFFFFF,
|
||||
group: 0
|
||||
}
|
||||
};
|
||||
|
||||
var mouseConstraint = Common.extend(defaults, options);
|
||||
|
@ -72,7 +77,8 @@ var MouseConstraint = {};
|
|||
for (var i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
if (Bounds.contains(body.bounds, mouse.position)
|
||||
&& Vertices.contains(body.vertices, mouse.position)) {
|
||||
&& Vertices.contains(body.vertices, mouse.position)
|
||||
&& Detector.canCollide(body.collisionFilter, mouseConstraint.collisionFilter)) {
|
||||
constraint.pointA = mouse.position;
|
||||
constraint.bodyB = body;
|
||||
constraint.pointB = { x: mouse.position.x - body.position.x, y: mouse.position.y - body.position.y };
|
||||
|
@ -196,4 +202,13 @@ var MouseConstraint = {};
|
|||
* @type constraint
|
||||
*/
|
||||
|
||||
/**
|
||||
* An `Object` that specifies the collision filter properties.
|
||||
* The collision filter allows the user to define which types of body this mouse constraint can interact with.
|
||||
* See `body.collisionFilter` for more information.
|
||||
*
|
||||
* @property collisionFilter
|
||||
* @type object
|
||||
*/
|
||||
|
||||
})();
|
|
@ -226,7 +226,7 @@ var Composites = {};
|
|||
* @return {composite} A new composite car body
|
||||
*/
|
||||
Composites.car = function(xx, yy, width, height, wheelSize) {
|
||||
var groupId = Body.nextGroupId(),
|
||||
var group = Body.nextGroup(true),
|
||||
wheelBase = -20,
|
||||
wheelAOffset = -width * 0.5 + wheelBase,
|
||||
wheelBOffset = width * 0.5 - wheelBase,
|
||||
|
@ -234,7 +234,9 @@ var Composites = {};
|
|||
|
||||
var car = Composite.create({ label: 'Car' }),
|
||||
body = Bodies.trapezoid(xx, yy, width, height, 0.3, {
|
||||
groupId: groupId,
|
||||
collisionFilter: {
|
||||
group: group
|
||||
},
|
||||
friction: 0.01,
|
||||
chamfer: {
|
||||
radius: 10
|
||||
|
@ -242,14 +244,18 @@ var Composites = {};
|
|||
});
|
||||
|
||||
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
|
||||
groupId: groupId,
|
||||
collisionFilter: {
|
||||
group: group
|
||||
},
|
||||
restitution: 0.5,
|
||||
friction: 0.9,
|
||||
density: 0.01
|
||||
});
|
||||
|
||||
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
|
||||
groupId: groupId,
|
||||
collisionFilter: {
|
||||
group: group
|
||||
},
|
||||
restitution: 0.5,
|
||||
friction: 0.9,
|
||||
density: 0.01
|
||||
|
|
Loading…
Reference in a new issue