0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-11-27 09:50:52 -05:00

Merge branch 'GustavCarlson-collision-filters'

This commit is contained in:
liabru 2014-07-29 15:31:07 +01:00
commit a58fe2a3c7
6 changed files with 222 additions and 41 deletions

View file

@ -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>
@ -67,4 +68,4 @@
<div id="canvas-container"></div>
</div>
</body>
</html>
</html>

View file

@ -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() {
@ -1400,4 +1491,4 @@
renderOptions.showDebug = true;
};
})();
})();

View file

@ -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.
*
@ -797,4 +852,4 @@ var Body = {};
* @type bounds
*/
})();
})();

View file

@ -26,13 +26,11 @@ 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;
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
continue;
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
continue;
metrics.midphaseTests += 1;
@ -87,11 +85,11 @@ var Detector = {};
// NOTE: could share a function for the below, but may drop performance?
if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId)
continue;
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
continue;
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;
};
})();

View file

@ -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
*/
})();

View file

@ -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
@ -308,4 +314,4 @@ var Composites = {};
return softBody;
};
})();
})();