diff --git a/build/p2.js b/build/p2.js
index e3d50ce4..cfd12f50 100644
--- a/build/p2.js
+++ b/build/p2.js
@@ -1530,7 +1530,7 @@ AABB.prototype.overlaps = function(aabb){
((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1]));
};
-},{"../math/vec2":33,"../utils/Utils":49}],10:[function(require,module,exports){
+},{"../math/vec2":33,"../utils/Utils":50}],10:[function(require,module,exports){
var vec2 = require('../math/vec2')
var Body = require('../objects/Body')
@@ -1541,7 +1541,9 @@ module.exports = Broadphase;
* @class Broadphase
* @constructor
*/
-function Broadphase(){
+function Broadphase(type){
+
+ this.type = type;
/**
* The resulting overlapping pairs. Will be filled with results during .getCollisionPairs().
@@ -1635,6 +1637,9 @@ Broadphase.canCollide = function(bodyA, bodyB){
return true;
};
+Broadphase.NAIVE = 1;
+Broadphase.SAP = 2;
+
},{"../math/vec2":33,"../objects/Body":34}],11:[function(require,module,exports){
var Circle = require('../shapes/Circle')
, Plane = require('../shapes/Plane')
@@ -1796,7 +1801,7 @@ GridBroadphase.prototype.getCollisionPairs = function(world){
return result;
};
-},{"../collision/Broadphase":10,"../math/vec2":33,"../shapes/Circle":38,"../shapes/Particle":41,"../shapes/Plane":42}],12:[function(require,module,exports){
+},{"../collision/Broadphase":10,"../math/vec2":33,"../shapes/Circle":38,"../shapes/Particle":42,"../shapes/Plane":43}],12:[function(require,module,exports){
var Circle = require('../shapes/Circle')
, Plane = require('../shapes/Plane')
, Shape = require('../shapes/Shape')
@@ -1814,7 +1819,7 @@ module.exports = NaiveBroadphase;
* @extends Broadphase
*/
function NaiveBroadphase(){
- Broadphase.apply(this);
+ Broadphase.call(this,Broadphase.NAIVE);
/**
* Set to true to use bounding box checks instead of bounding radius.
@@ -1853,7 +1858,7 @@ NaiveBroadphase.prototype.getCollisionPairs = function(world){
return result;
};
-},{"../collision/Broadphase":10,"../math/vec2":33,"../shapes/Circle":38,"../shapes/Particle":41,"../shapes/Plane":42,"../shapes/Shape":44}],13:[function(require,module,exports){
+},{"../collision/Broadphase":10,"../math/vec2":33,"../shapes/Circle":38,"../shapes/Particle":42,"../shapes/Plane":43,"../shapes/Shape":45}],13:[function(require,module,exports){
var vec2 = require('../math/vec2')
, sub = vec2.sub
, add = vec2.add
@@ -1864,6 +1869,7 @@ var vec2 = require('../math/vec2')
, Circle = require('../shapes/Circle')
, Shape = require('../shapes/Shape')
, Body = require('../objects/Body')
+, Rectangle = require('../shapes/Rectangle')
module.exports = Narrowphase;
@@ -2124,27 +2130,15 @@ Narrowphase.prototype.lineRectangle = function(bi,si,xi,ai, bj,sj,xj,aj, justTes
return 0;
};
-/**
- * Rectangle/capsule narrowphase
- * @method rectangleCapsule
- * @param {Body} bi
- * @param {Rectangle} si
- * @param {Array} xi
- * @param {Number} ai
- * @param {Body} bj
- * @param {Capsule} sj
- * @param {Array} xj
- * @param {Number} aj
- * @todo Implement me!
- */
-Narrowphase.prototype[Shape.CAPSULE | Shape.RECTANGLE] =
-Narrowphase.prototype.rectangleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
- // TODO
- if(justTest)
- return false;
- else
- return 0;
-};
+function setConvexToCapsuleShapeMiddle(convexShape, capsuleShape){
+ vec2.set(convexShape.vertices[0], -capsuleShape.length * 0.5, -capsuleShape.radius);
+ vec2.set(convexShape.vertices[1], capsuleShape.length * 0.5, -capsuleShape.radius);
+ vec2.set(convexShape.vertices[2], capsuleShape.length * 0.5, capsuleShape.radius);
+ vec2.set(convexShape.vertices[3], -capsuleShape.length * 0.5, capsuleShape.radius);
+}
+
+var convexCapsule_tempRect = new Rectangle(1,1),
+ convexCapsule_tempVec = vec2.create();
/**
* Convex/capsule narrowphase
@@ -2160,12 +2154,31 @@ Narrowphase.prototype.rectangleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, just
* @todo Implement me!
*/
Narrowphase.prototype[Shape.CAPSULE | Shape.CONVEX] =
+Narrowphase.prototype[Shape.CAPSULE | Shape.RECTANGLE] =
Narrowphase.prototype.convexCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
- // TODO
- if(justTest)
- return false;
- else
- return 0;
+
+ // Check the circles
+ // Add offsets!
+ var circlePos = convexCapsule_tempVec;
+ vec2.set(circlePos, sj.length/2,0);
+ vec2.rotate(circlePos,circlePos,aj);
+ vec2.add(circlePos,circlePos,xj);
+ var result1 = this.circleConvex(bj,sj,circlePos,aj, bi,si,xi,ai, justTest, sj.radius);
+
+ vec2.set(circlePos,-sj.length/2, 0);
+ vec2.rotate(circlePos,circlePos,aj);
+ vec2.add(circlePos,circlePos,xj);
+ var result2 = this.circleConvex(bj,sj,circlePos,aj, bi,si,xi,ai, justTest, sj.radius);
+
+ if(justTest && (result1 || result2))
+ return true;
+
+ // Check center rect
+ var r = convexCapsule_tempRect;
+ setConvexToCapsuleShapeMiddle(r,sj);
+ var result = this.convexConvex(bi,si,xi,ai, bj,r,xj,aj, justTest);
+
+ return result + result1 + result2;
};
/**
@@ -2190,6 +2203,10 @@ Narrowphase.prototype.lineCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest)
return 0;
};
+var capsuleCapsule_tempVec1 = vec2.create();
+var capsuleCapsule_tempVec2 = vec2.create();
+var capsuleCapsule_tempRect1 = new Rectangle(1,1);
+
/**
* Capsule/capsule narrowphase
* @method capsuleCapsule
@@ -2205,11 +2222,51 @@ Narrowphase.prototype.lineCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest)
*/
Narrowphase.prototype[Shape.CAPSULE | Shape.CAPSULE] =
Narrowphase.prototype.capsuleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
- // TODO
- if(justTest)
- return false;
- else
- return 0;
+
+ // Check the circles
+ // Add offsets!
+ var circlePosi = capsuleCapsule_tempVec1,
+ circlePosj = capsuleCapsule_tempVec2;
+
+ var numContacts = 0;
+
+ // Need 4 circle checks, between all
+ for(var i=0; i<2; i++){
+
+ vec2.set(circlePosi,(i==0?-1:1)*si.length/2,0);
+ vec2.rotate(circlePosi,circlePosi,ai);
+ vec2.add(circlePosi,circlePosi,xi);
+
+ for(var j=0; j<2; j++){
+
+ vec2.set(circlePosj,(j==0?-1:1)*sj.length/2, 0);
+ vec2.rotate(circlePosj,circlePosj,aj);
+ vec2.add(circlePosj,circlePosj,xj);
+
+ var result = this.circleCircle(bi,si,circlePosi,ai, bj,sj,circlePosj,aj, justTest, si.radius, sj.radius);
+
+ if(justTest && result)
+ return true;
+
+ numContacts += result;
+ }
+ }
+
+ // Check circles against the center rectangles
+ var rect = capsuleCapsule_tempRect1;
+ setConvexToCapsuleShapeMiddle(rect,si);
+ var result1 = this.convexCapsule(bi,rect,xi,ai, bj,sj,xj,aj, justTest);
+
+ if(justTest && result1) return true;
+ numContacts += result1;
+
+ setConvexToCapsuleShapeMiddle(rect,sj);
+ var result2 = this.convexCapsule(bj,rect,xj,aj, bi,si,xi,ai, justTest);
+
+ if(justTest && result2) return true;
+ numContacts += result2;
+
+ return numContacts;
};
/**
@@ -2525,6 +2582,7 @@ Narrowphase.prototype.circleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTes
* @param {Number} aj
*/
Narrowphase.prototype[Shape.CIRCLE | Shape.CONVEX] =
+Narrowphase.prototype[Shape.CIRCLE | Shape.RECTANGLE] =
Narrowphase.prototype.circleConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, circleRadius){
var convexShape = sj,
convexAngle = aj,
@@ -2569,8 +2627,8 @@ Narrowphase.prototype.circleConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTe
verts = convexShape.vertices;
// Check all edges first
- for(var i=0; i!==verts.length; i++){
- var v0 = verts[i],
+ for(var i=0; i!==verts.length+1; i++){
+ var v0 = verts[i%verts.length],
v1 = verts[(i+1)%verts.length];
vec2.rotate(worldVertex0, v0, convexAngle);
@@ -2780,6 +2838,7 @@ function pointInConvex(worldPoint,convexShape,convexOffset,convexAngle){
* @todo use pointInConvex and code more similar to circleConvex
*/
Narrowphase.prototype[Shape.PARTICLE | Shape.CONVEX] =
+Narrowphase.prototype[Shape.PARTICLE | Shape.RECTANGLE] =
Narrowphase.prototype.particleConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
var convexShape = sj,
convexAngle = aj,
@@ -2926,17 +2985,19 @@ Narrowphase.prototype.particleConvex = function( bi,si,xi,ai, bj,sj,xj,aj, just
* @param {Number} aj
*/
Narrowphase.prototype[Shape.CIRCLE] =
-Narrowphase.prototype.circleCircle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest){
+Narrowphase.prototype.circleCircle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, radiusA, radiusB){
var bodyA = bi,
shapeA = si,
offsetA = xi,
bodyB = bj,
shapeB = sj,
offsetB = xj,
- dist = tmp1;
+ dist = tmp1,
+ radiusA = radiusA || shapeA.radius,
+ radiusB = radiusB || shapeB.radius;
sub(dist,xi,xj);
- var r = si.radius + sj.radius;
+ var r = radiusA + radiusB;
if(vec2.squaredLength(dist) > r*r)
return 0;
@@ -2946,8 +3007,8 @@ Narrowphase.prototype.circleCircle = function( bi,si,xi,ai, bj,sj,xj,aj, justTe
sub(c.ni, offsetB, offsetA);
vec2.normalize(c.ni,c.ni);
- vec2.scale( c.ri, c.ni, shapeA.radius);
- vec2.scale( c.rj, c.ni, -shapeB.radius);
+ vec2.scale( c.ri, c.ni, radiusA);
+ vec2.scale( c.rj, c.ni, -radiusB);
add(c.ri, c.ri, offsetA);
sub(c.ri, c.ri, bodyA.position);
@@ -2976,6 +3037,7 @@ Narrowphase.prototype.circleCircle = function( bi,si,xi,ai, bj,sj,xj,aj, justTe
* @param {Number} aj
*/
Narrowphase.prototype[Shape.PLANE | Shape.CONVEX] =
+Narrowphase.prototype[Shape.PLANE | Shape.RECTANGLE] =
Narrowphase.prototype.planeConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
var convexBody = bj,
convexOffset = xj,
@@ -3260,6 +3322,7 @@ Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTe
return 1;
};
+Narrowphase.convexPrecision = 1e-10;
/**
* Convex/convex Narrowphase.See this article for more info.
@@ -3274,6 +3337,8 @@ Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTe
* @param {Number} aj
*/
Narrowphase.prototype[Shape.CONVEX] =
+Narrowphase.prototype[Shape.CONVEX | Shape.RECTANGLE] =
+Narrowphase.prototype[Shape.RECTANGLE] =
Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, precision ){
var sepAxis = tmp1,
worldPoint = tmp2,
@@ -3285,7 +3350,7 @@ Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTe
dist = tmp8,
worldNormal = tmp9,
numContacts = 0,
- precision = precision || 1e-10;
+ precision = precision || Narrowphase.convexPrecision;
var found = Narrowphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis);
if(!found) return 0;
@@ -3579,8 +3644,182 @@ Narrowphase.getClosestEdge = function(c,angle,axis,flip){
return closestEdge;
};
+var circleHeightfield_candidate = vec2.create(),
+ circleHeightfield_dist = vec2.create(),
+ circleHeightfield_v0 = vec2.create(),
+ circleHeightfield_v1 = vec2.create(),
+ circleHeightfield_minCandidate = vec2.create(),
+ circleHeightfield_worldNormal = vec2.create(),
+ circleHeightfield_minCandidateNormal = vec2.create();
-},{"../equations/ContactEquation":23,"../equations/FrictionEquation":25,"../math/vec2":33,"../objects/Body":34,"../shapes/Circle":38,"../shapes/Shape":44,"../utils/Utils":49}],14:[function(require,module,exports){
+/**
+ * @method circleHeightfield
+ * @param {Body} bi
+ * @param {Circle} si
+ * @param {Array} xi
+ * @param {Body} bj
+ * @param {Heightfield} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.CIRCLE | Shape.HEIGHTFIELD] =
+Narrowphase.prototype.circleHeightfield = function( circleBody,circleShape,circlePos,circleAngle,
+ hfBody,hfShape,hfPos,hfAngle, justTest, radius ){
+ var data = hfShape.data,
+ radius = radius || circleShape.radius,
+ w = hfShape.elementWidth,
+ dist = circleHeightfield_dist,
+ candidate = circleHeightfield_candidate,
+ minCandidate = circleHeightfield_minCandidate,
+ minCandidateNormal = circleHeightfield_minCandidateNormal,
+ worldNormal = circleHeightfield_worldNormal,
+ v0 = circleHeightfield_v0,
+ v1 = circleHeightfield_v1;
+
+ // Get the index of the points to test against
+ var idxA = Math.floor( (circlePos[0] - radius - hfPos[0]) / w ),
+ idxB = Math.ceil( (circlePos[0] + radius - hfPos[0]) / w );
+
+ /*if(idxB < 0 || idxA >= data.length)
+ return justTest ? false : 0;*/
+
+ if(idxA < 0) idxA = 0;
+ if(idxB >= data.length) idxB = data.length-1;
+
+ // Get max and min
+ var max = data[idxA],
+ min = data[idxB];
+ for(var i=idxA; i max) max = data[i];
+ }
+
+ if(circlePos[1]-radius > max)
+ return justTest ? false : 0;
+
+ if(circlePos[1]+radius < min){
+ // Below the minimum point... We can just guess.
+ // TODO
+ }
+
+ // 1. Check so center of circle is not inside the field. If it is, this wont work...
+ // 2. For each edge
+ // 2. 1. Get point on circle that is closest to the edge (scale normal with -radius)
+ // 2. 2. Check if point is inside.
+
+ var found = false,
+ minDist = false;
+
+ // Check all edges first
+ for(var i=idxA; i= v0[0] && candidate[0] < v1[0] && d <= 0){
+
+ if(minDist === false || Math.abs(d) < minDist){
+
+ // Store the candidate point, projected to the edge
+ vec2.scale(dist,worldNormal,-d);
+ vec2.add(minCandidate,candidate,dist);
+ vec2.copy(minCandidateNormal,worldNormal);
+
+ found = true;
+ minDist = Math.abs(d);
+
+ if(justTest)
+ return true;
+ }
+ }
+ }
+
+ if(found){
+
+ var c = this.createContactEquation(hfBody,circleBody,hfShape,circleShape);
+
+ // Normal is out of the heightfield
+ vec2.copy(c.ni, minCandidateNormal);
+
+ // Vector from circle to heightfield
+ vec2.scale(c.rj, c.ni, -radius);
+ add(c.rj, c.rj, circlePos);
+ sub(c.rj, c.rj, circleBody.position);
+
+ vec2.copy(c.ri, minCandidate);
+ //vec2.sub(c.ri, c.ri, hfPos);
+ vec2.sub(c.ri, c.ri, hfBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction)
+ this.frictionEquations.push( this.createFrictionFromContact(c) );
+
+ return 1;
+ }
+
+
+ // Check all vertices
+ if(radius > 0){
+ for(var i=idxA; i<=idxB; i++){
+
+ // Get point
+ vec2.set(v0, i*w, data[i]);
+ vec2.add(v0,v0,hfPos);
+
+ vec2.sub(dist, circlePos, v0);
+
+ if(vec2.squaredLength(dist) < radius*radius){
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(hfBody,circleBody,hfShape,circleShape);
+
+ // Construct normal - out of heightfield
+ vec2.copy(c.ni, dist);
+ vec2.normalize(c.ni,c.ni);
+
+ vec2.scale(c.rj, c.ni, -radius);
+ add(c.rj, c.rj, circlePos);
+ sub(c.rj, c.rj, circleBody.position);
+
+ sub(c.ri, v0, hfPos);
+ add(c.ri, c.ri, hfPos);
+ sub(c.ri, c.ri, hfBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+
+};
+
+},{"../equations/ContactEquation":23,"../equations/FrictionEquation":25,"../math/vec2":33,"../objects/Body":34,"../shapes/Circle":38,"../shapes/Rectangle":44,"../shapes/Shape":45,"../utils/Utils":50}],14:[function(require,module,exports){
var Plane = require("../shapes/Plane");
var Broadphase = require("../collision/Broadphase");
@@ -3958,7 +4197,7 @@ BoundsNode.prototype.clear = function(){
}
-},{"../collision/Broadphase":10,"../shapes/Plane":42}],15:[function(require,module,exports){
+},{"../collision/Broadphase":10,"../shapes/Plane":43}],15:[function(require,module,exports){
var Circle = require('../shapes/Circle')
, Plane = require('../shapes/Plane')
, Shape = require('../shapes/Shape')
@@ -3977,7 +4216,7 @@ module.exports = SAPBroadphase;
* @extends Broadphase
*/
function SAPBroadphase(){
- Broadphase.apply(this);
+ Broadphase.call(this,Broadphase.SAP);
/**
* List of bodies currently in the broadphase.
@@ -4183,7 +4422,7 @@ SAPBroadphase.checkBounds = function(bi,bj,axisIndex){
return bj.aabb.lowerBound[axisIndex] < bi.aabb.upperBound[axisIndex];
};
-},{"../collision/Broadphase":10,"../math/vec2":33,"../shapes/Circle":38,"../shapes/Particle":41,"../shapes/Plane":42,"../shapes/Shape":44,"../utils/Utils":49}],16:[function(require,module,exports){
+},{"../collision/Broadphase":10,"../math/vec2":33,"../shapes/Circle":38,"../shapes/Particle":42,"../shapes/Plane":43,"../shapes/Shape":45,"../utils/Utils":50}],16:[function(require,module,exports){
module.exports = Constraint;
/**
@@ -4195,7 +4434,9 @@ module.exports = Constraint;
* @param {Body} bodyA
* @param {Body} bodyB
*/
-function Constraint(bodyA,bodyB){
+function Constraint(bodyA,bodyB,type){
+
+ this.type = type;
/**
* Equations to be solved in this constraint
@@ -4230,6 +4471,12 @@ function Constraint(bodyA,bodyB){
throw new Error("method update() not implmemented in this Constraint subclass!");
};*/
+Constraint.DISTANCE = 1;
+Constraint.GEAR = 2;
+Constraint.LOCK = 3;
+Constraint.PRISMATIC = 4;
+Constraint.REVOLUTE = 5;
+
},{}],17:[function(require,module,exports){
var Constraint = require('./Constraint')
, Equation = require('../equations/Equation')
@@ -4250,7 +4497,7 @@ module.exports = DistanceConstraint;
* @extends {Constraint}
*/
function DistanceConstraint(bodyA,bodyB,distance,maxForce){
- Constraint.call(this,bodyA,bodyB);
+ Constraint.call(this,bodyA,bodyB,Constraint.DISTANCE);
/**
* The distance to keep.
@@ -4337,7 +4584,7 @@ module.exports = GearConstraint;
* @todo Ability to specify world points
*/
function GearConstraint(bodyA, bodyB, options){
- Constraint.call(this,bodyA,bodyB);
+ Constraint.call(this,bodyA,bodyB,Constraint.GEAR);
// Equations to be fed to the solver
var eqs = this.equations = [
@@ -4389,7 +4636,7 @@ module.exports = LockConstraint;
* @extends {Constraint}
*/
function LockConstraint(bodyA,bodyB,options){
- Constraint.call(this,bodyA,bodyB);
+ Constraint.call(this,bodyA,bodyB,Constraint.LOCK);
var maxForce = ( typeof(options.maxForce)=="undefined" ? Number.MAX_VALUE : options.maxForce );
var localOffsetB = options.localOffsetB || vec2.fromValues(0,0);
localOffsetB = vec2.fromValues(localOffsetB[0],localOffsetB[1]);
@@ -4519,7 +4766,7 @@ module.exports = PrismaticConstraint;
*/
function PrismaticConstraint(bodyA,bodyB,options){
options = options || {};
- Constraint.call(this,bodyA,bodyB);
+ Constraint.call(this,bodyA,bodyB,Constraint.PRISMATIC);
// Get anchors
var localAnchorA = vec2.fromValues(0,0),
@@ -4834,7 +5081,7 @@ var worldPivotA = vec2.create(),
* @todo Ability to specify world points
*/
function RevoluteConstraint(bodyA, pivotA, bodyB, pivotB, maxForce){
- Constraint.call(this,bodyA,bodyB);
+ Constraint.call(this,bodyA,bodyB,Constraint.REVOLUTE);
maxForce = this.maxForce = typeof(maxForce)!="undefined" ? maxForce : Number.MAX_VALUE;
@@ -5268,6 +5515,7 @@ function Equation(bi,bj,minForce,maxForce){
* @type {Array}
*/
this.G = new Utils.ARRAY_TYPE(6);
+ for(var i=0; i<6; i++) this.G[i]=0;
// Constraint frames for body i and j
/*
@@ -5548,7 +5796,7 @@ Equation.prototype.computeInvC = function(eps){
return 1.0 / (this.computeGiMGt() + eps);
};
-},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":49}],25:[function(require,module,exports){
+},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":50}],25:[function(require,module,exports){
var mat2 = require('../math/mat2')
, vec2 = require('../math/vec2')
, Equation = require('./Equation')
@@ -5660,7 +5908,7 @@ FrictionEquation.prototype.computeB = function(a,b,h){
return B;
};
-},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":49,"./Equation":24}],26:[function(require,module,exports){
+},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":50,"./Equation":24}],26:[function(require,module,exports){
var Equation = require("./Equation"),
vec2 = require('../math/vec2');
@@ -5822,30 +6070,38 @@ EventEmitter.prototype = {
};
},{}],29:[function(require,module,exports){
+var Material = require('./Material');
+
module.exports = ContactMaterial;
-var idCounter = 0;
-
/**
- * Defines a physics material.
+ * Defines what happens when two materials meet, such as what friction coefficient to use. You can also set other things such as restitution, surface velocity and constraint parameters.
* @class ContactMaterial
* @constructor
* @param {Material} materialA
* @param {Material} materialB
* @param {Object} [options]
- * @param {Number} options.friction
- * @param {Number} options.restitution
+ * @param {Number} options.friction Friction coefficient.
+ * @param {Number} options.restitution Restitution coefficient aka "bounciness".
+ * @param {Number} options.stiffness ContactEquation stiffness.
+ * @param {Number} options.relaxation ContactEquation relaxation.
+ * @param {Number} options.frictionStiffness FrictionEquation stiffness.
+ * @param {Number} options.frictionRelaxation FrictionEquation relaxation.
+ * @param {Number} options.surfaceVelocity Surface velocity.
* @author schteppe
*/
function ContactMaterial(materialA, materialB, options){
options = options || {};
+ if(!(materialA instanceof Material) || !(materialB instanceof Material))
+ throw new Error("First two arguments must be Material instances.");
+
/**
* The contact material identifier
* @property id
* @type {Number}
*/
- this.id = idCounter++;
+ this.id = ContactMaterial.idCounter++;
/**
* First material participating in the contact material
@@ -5910,10 +6166,10 @@ function ContactMaterial(materialA, materialB, options){
this.surfaceVelocity = typeof(options.surfaceVelocity) !== "undefined" ? Number(options.surfaceVelocity) : 0
};
-},{}],30:[function(require,module,exports){
-module.exports = Material;
+ContactMaterial.idCounter = 0;
-var idCounter = 0;
+},{"./Material":30}],30:[function(require,module,exports){
+module.exports = Material;
/**
* Defines a physics material.
@@ -5928,9 +6184,11 @@ function Material(){
* @property id
* @type {Number}
*/
- this.id = idCounter++;
+ this.id = Material.idCounter++;
};
+Material.idCounter = 0;
+
},{}],31:[function(require,module,exports){
/**
* The mat2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
@@ -6561,6 +6819,7 @@ module.exports = Body;
*
* @class Body
* @constructor
+ * @extends {EventEmitter}
* @param {Object} [options]
* @param {Number} [options.mass=0] A number >= 0. If zero, the .motionState will be set to Body.STATIC.
* @param {Float32Array|Array} [options.position]
@@ -6570,8 +6829,6 @@ module.exports = Body;
* @param {Float32Array|Array} [options.force]
* @param {Number} [options.angularForce=0]
* @param {Number} [options.fixedRotation=false]
- *
- * @todo Should not take mass as argument to Body, but as density to each Shape
*/
function Body(options){
options = options || {};
@@ -6651,8 +6908,6 @@ function Body(options){
*/
this.fixedRotation = !!options.fixedRotation || false;
- this.updateMassProperties();
-
/**
* The position of the body
* @property position
@@ -6820,8 +7075,9 @@ function Body(options){
this.lastDampingScale = 1;
this.lastAngularDampingScale = 1;
this.lastDampingTimeStep = -1;
-};
+ this.updateMassProperties();
+};
Body.prototype = new EventEmitter();
Body._idCounter = 0;
@@ -6977,25 +7233,38 @@ Body.prototype.removeShape = function(shape){
* body.updateMassProperties();
*/
Body.prototype.updateMassProperties = function(){
- var shapes = this.shapes,
- N = shapes.length,
- m = this.mass / N,
- I = 0;
+ if(this.motionState == Body.STATIC || this.motionState == Body.KINEMATIC){
- if(!this.fixedRotation){
- for(var i=0; i0 ? 1/I : 0;
+
+ } else {
+ this.inertia = Number.MAX_VALUE;
+ this.invInertia = 0;
}
+
+ // Inverse mass properties are easy
+ this.invMass = 1/this.mass;// > 0 ? 1/this.mass : 0;
}
-
- this.inertia = I;
-
- // Inverse mass properties are easy
- this.invMass = this.mass > 0 ? 1/this.mass : 0;
- this.invInertia = I>0 ? 1/I : 0;
};
var Body_applyForce_r = vec2.create();
@@ -7254,14 +7523,23 @@ Body.prototype.sleepTick = function(time){
}
};
+/**
+ * @event sleepy
+ */
Body.sleepyEvent = {
type: "sleepy"
};
+/**
+ * @event sleep
+ */
Body.sleepEvent = {
type: "sleep"
};
+/**
+ * @event wakeup
+ */
Body.wakeUpEvent = {
type: "wakeup"
};
@@ -7515,11 +7793,13 @@ module.exports = {
GearConstraint : require('./constraints/GearConstraint'),
GridBroadphase : require('./collision/GridBroadphase'),
GSSolver : require('./solver/GSSolver'),
+ Heightfield : require('./shapes/Heightfield'),
Island : require('./solver/IslandSolver'),
IslandSolver : require('./solver/IslandSolver'),
Line : require('./shapes/Line'),
LockConstraint : require('./constraints/LockConstraint'),
Material : require('./material/Material'),
+ Narrowphase : require('./collision/Narrowphase'),
NaiveBroadphase : require('./collision/NaiveBroadphase'),
Particle : require('./shapes/Particle'),
Plane : require('./shapes/Plane'),
@@ -7538,7 +7818,7 @@ module.exports = {
version : require('../package.json').version,
};
-},{"../package.json":8,"./collision/AABB":9,"./collision/Broadphase":10,"./collision/GridBroadphase":11,"./collision/NaiveBroadphase":12,"./collision/QuadTree":14,"./collision/SAPBroadphase":15,"./constraints/Constraint":16,"./constraints/DistanceConstraint":17,"./constraints/GearConstraint":18,"./constraints/LockConstraint":19,"./constraints/PrismaticConstraint":20,"./constraints/RevoluteConstraint":21,"./equations/AngleLockEquation":22,"./equations/ContactEquation":23,"./equations/Equation":24,"./equations/FrictionEquation":25,"./equations/RotationalVelocityEquation":27,"./events/EventEmitter":28,"./material/ContactMaterial":29,"./material/Material":30,"./math/vec2":33,"./objects/Body":34,"./objects/Spring":35,"./shapes/Capsule":37,"./shapes/Circle":38,"./shapes/Convex":39,"./shapes/Line":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Rectangle":43,"./shapes/Shape":44,"./solver/GSSolver":45,"./solver/IslandSolver":47,"./solver/Solver":48,"./utils/Utils":49,"./world/World":50}],37:[function(require,module,exports){
+},{"../package.json":8,"./collision/AABB":9,"./collision/Broadphase":10,"./collision/GridBroadphase":11,"./collision/NaiveBroadphase":12,"./collision/Narrowphase":13,"./collision/QuadTree":14,"./collision/SAPBroadphase":15,"./constraints/Constraint":16,"./constraints/DistanceConstraint":17,"./constraints/GearConstraint":18,"./constraints/LockConstraint":19,"./constraints/PrismaticConstraint":20,"./constraints/RevoluteConstraint":21,"./equations/AngleLockEquation":22,"./equations/ContactEquation":23,"./equations/Equation":24,"./equations/FrictionEquation":25,"./equations/RotationalVelocityEquation":27,"./events/EventEmitter":28,"./material/ContactMaterial":29,"./material/Material":30,"./math/vec2":33,"./objects/Body":34,"./objects/Spring":35,"./shapes/Capsule":37,"./shapes/Circle":38,"./shapes/Convex":39,"./shapes/Heightfield":40,"./shapes/Line":41,"./shapes/Particle":42,"./shapes/Plane":43,"./shapes/Rectangle":44,"./shapes/Shape":45,"./solver/GSSolver":46,"./solver/IslandSolver":48,"./solver/Solver":49,"./utils/Utils":50,"./world/World":51}],37:[function(require,module,exports){
var Shape = require('./Shape')
, vec2 = require('../math/vec2')
@@ -7553,7 +7833,17 @@ module.exports = Capsule;
* @param {Number} radius Radius of the capsule
*/
function Capsule(length,radius){
+
+ /**
+ * The distance between the end points.
+ * @property {Number} length
+ */
this.length = length || 1;
+
+ /**
+ * The radius of the capsule.
+ * @property {Number} radius
+ */
this.radius = radius || 1;
Shape.call(this,Shape.CAPSULE);
@@ -7575,10 +7865,16 @@ Capsule.prototype.computeMomentOfInertia = function(mass){
return mass * (h*h + w*w) / 12;
};
+/**
+ * @method updateBoundingRadius
+ */
Capsule.prototype.updateBoundingRadius = function(){
this.boundingRadius = this.radius + this.length/2;
};
+/**
+ * @method updateArea
+ */
Capsule.prototype.updateArea = function(){
this.area = Math.PI * this.radius * this.radius + this.radius * 2 * this.length;
};
@@ -7609,7 +7905,7 @@ Capsule.prototype.computeAABB = function(out, position, angle){
vec2.add(out.upperBound, out.upperBound, position);
};
-},{"../math/vec2":33,"./Shape":44}],38:[function(require,module,exports){
+},{"../math/vec2":33,"./Shape":45}],38:[function(require,module,exports){
var Shape = require('./Shape')
, vec2 = require('../math/vec2')
@@ -7634,6 +7930,12 @@ function Circle(radius){
Shape.call(this,Shape.CIRCLE);
};
Circle.prototype = new Shape();
+
+/**
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ */
Circle.prototype.computeMomentOfInertia = function(mass){
var r = this.radius;
return mass * r * r / 2;
@@ -7663,7 +7965,7 @@ Circle.prototype.computeAABB = function(out, position, angle){
}
};
-},{"../math/vec2":33,"./Shape":44}],39:[function(require,module,exports){
+},{"../math/vec2":33,"./Shape":45}],39:[function(require,module,exports){
var Shape = require('./Shape')
, vec2 = require('../math/vec2')
, polyk = require('../math/polyk')
@@ -7685,13 +7987,13 @@ function Convex(vertices){
* @property vertices
* @type {Array}
*/
- this.vertices = vertices || [];
+ this.vertices = [];
// Copy the verts
- for(var i=0; iWikipedia's list of moments of inertia.
* @method computeMomentOfInertia
@@ -8287,7 +8672,7 @@ Shape.prototype.computeAABB = function(out, position, angle){
// To be implemented in each subclass
};
-},{}],45:[function(require,module,exports){
+},{}],46:[function(require,module,exports){
var vec2 = require('../math/vec2')
, Solver = require('./Solver')
, Utils = require('../utils/Utils')
@@ -8309,7 +8694,7 @@ module.exports = GSSolver;
* @param {Number} options.tolerance
*/
function GSSolver(options){
- Solver.call(this,options);
+ Solver.call(this,options,Solver.GS);
options = options || {};
/**
@@ -8360,6 +8745,8 @@ function GSSolver(options){
*/
this.useZeroRHS = false;
+ this.useNormalForceForFriction = false;
+
/**
* Number of friction iterations to skip. If .skipFrictionIterations=2, then no FrictionEquations will be iterated until the third iteration.
* @property skipFrictionIterations
@@ -8409,9 +8796,8 @@ GSSolver.prototype.solve = function(h,world){
lambda = this.lambda = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
this.Bs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
this.invCs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
- } else {
- setArrayZero(lambda);
}
+ setArrayZero(lambda);
var invCs = this.invCs,
Bs = this.Bs,
lambda = this.lambda;
@@ -8451,7 +8837,7 @@ GSSolver.prototype.solve = function(h,world){
var _eps = useGlobalParams ? eps : c.eps;
- var deltalambda = GSSolver.iterateEquation(j,c,_eps,Bs,invCs,lambda,useZeroRHS,h);
+ var deltalambda = GSSolver.iterateEquation(j,c,_eps,Bs,invCs,lambda,useZeroRHS,h,iter,skipFrictionIter,this.useNormalForceForFriction);
deltalambdaTot += Math.abs(deltalambda);
}
@@ -8466,14 +8852,14 @@ GSSolver.prototype.solve = function(h,world){
}
};
-GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt){
+GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt,iter,skipFrictionIter,useNormal){
// Compute iteration
var B = Bs[j],
invC = invCs[j],
lambdaj = lambda[j],
- GWlambda = eq.computeGWlambda(eps);
+ GWlambda = eq.computeGWlambda();
- if(eq instanceof FrictionEquation){
+ if(useNormal && eq instanceof FrictionEquation && iter == skipFrictionIter){
// Rescale the max friction force according to the normal force
eq.maxForce = eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
eq.minForce = -eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
@@ -8488,10 +8874,10 @@ GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt){
// Clamp if we are not within the min/max interval
var lambdaj_plus_deltalambda = lambdaj + deltalambda;
- if(lambdaj_plus_deltalambda < minForce){
- deltalambda = minForce - lambdaj;
- } else if(lambdaj_plus_deltalambda > maxForce){
- deltalambda = maxForce - lambdaj;
+ if(lambdaj_plus_deltalambda < minForce*dt){
+ deltalambda = minForce*dt - lambdaj;
+ } else if(lambdaj_plus_deltalambda > maxForce*dt){
+ deltalambda = maxForce*dt - lambdaj;
}
lambda[j] += deltalambda;
eq.multiplier = lambda[j] / dt;
@@ -8500,7 +8886,7 @@ GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt){
return deltalambda;
};
-},{"../equations/FrictionEquation":25,"../math/vec2":33,"../utils/Utils":49,"./Solver":48}],46:[function(require,module,exports){
+},{"../equations/FrictionEquation":25,"../math/vec2":33,"../utils/Utils":50,"./Solver":49}],47:[function(require,module,exports){
module.exports = Island;
/**
@@ -8583,7 +8969,7 @@ Island.prototype.solve = function(dt,solver){
solver.solve(dt,{bodies:bodies});
};
-},{}],47:[function(require,module,exports){
+},{}],48:[function(require,module,exports){
var Solver = require('./Solver')
, vec2 = require('../math/vec2')
, Island = require('../solver/Island')
@@ -8602,7 +8988,7 @@ module.exports = IslandSolver;
* @extends Solver
*/
function IslandSolver(subsolver,options){
- Solver.call(this,options);
+ Solver.call(this,options,Solver.ISLAND);
var that = this;
/**
@@ -8778,7 +9164,7 @@ IslandSolver.prototype.solve = function(dt,world){
}
};
-},{"../math/vec2":33,"../objects/Body":34,"../solver/Island":46,"./Solver":48}],48:[function(require,module,exports){
+},{"../math/vec2":33,"../objects/Body":34,"../solver/Island":47,"./Solver":49}],49:[function(require,module,exports){
var Utils = require('../utils/Utils')
, EventEmitter = require('../events/EventEmitter')
@@ -8790,11 +9176,13 @@ module.exports = Solver;
* @constructor
* @extends {EventEmitter}
*/
-function Solver(options){
+function Solver(options,type){
options = options || {};
EventEmitter.call(this);
+ this.type = type;
+
/**
* Current equations in the solver.
*
@@ -8878,8 +9266,10 @@ Solver.prototype.removeAllEquations = function(){
this.equations.length=0;
};
+Solver.GS=1;
+Solver.ISLAND=2;
-},{"../events/EventEmitter":28,"../utils/Utils":49}],49:[function(require,module,exports){
+},{"../events/EventEmitter":28,"../utils/Utils":50}],50:[function(require,module,exports){
module.exports = Utils;
/**
@@ -8929,7 +9319,7 @@ Utils.splice = function(array,index,howmany){
*/
Utils.ARRAY_TYPE = Float32Array || Array;
-},{}],50:[function(require,module,exports){
+},{}],51:[function(require,module,exports){
var GSSolver = require('../solver/GSSolver')
, NaiveBroadphase = require('../collision/NaiveBroadphase')
, vec2 = require('../math/vec2')
@@ -8976,9 +9366,10 @@ if(!performance.now){
* @class World
* @constructor
* @param {Object} [options]
- * @param {Solver} options.solver Defaults to GSSolver.
- * @param {Float32Array} options.gravity Defaults to [0,-9.78]
- * @param {Broadphase} options.broadphase Defaults to NaiveBroadphase
+ * @param {Solver} options.solver Defaults to GSSolver.
+ * @param {Float32Array} options.gravity Defaults to [0,-9.78]
+ * @param {Broadphase} options.broadphase Defaults to NaiveBroadphase
+ * @param {Boolean} options.doProfiling
* @extends {EventEmitter}
*/
function World(options){
@@ -9207,7 +9598,7 @@ function World(options){
this.enableBodySleeping = false;
/**
- * Fired when two shapes starts start to touch (and create contacts).
+ * Fired when two shapes starts start to overlap. Fired in the narrowphase, during step.
* @event beginContact
* @param {Shape} shapeA
* @param {Shape} shapeB
@@ -9225,7 +9616,7 @@ function World(options){
};
/**
- * Fired when two shapes stop touching.
+ * Fired when two shapes stop overlapping, after the narrowphase (during step).
* @event endContact
* @param {Shape} shapeA
* @param {Shape} shapeB
@@ -9417,11 +9808,15 @@ World.prototype.internalStep = function(dt){
t0 = performance.now();
}
+ var glen = vec2.length(this.gravity);
+
// Add gravity to bodies
if(this.applyGravity){
for(var i=0; i!==Nbodies; i++){
var b = bodies[i],
fi = b.force;
+ if(b.motionState != Body.DYNAMIC)
+ continue;
vec2.scale(mg,g,b.mass*b.gravityScale); // F=m*g
add(fi,fi,mg);
}
@@ -9481,7 +9876,7 @@ World.prototype.internalStep = function(dt){
}
}
- this.runNarrowphase(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitution,surfaceVelocity);
+ this.runNarrowphase(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitution,surfaceVelocity,glen);
}
}
}
@@ -9636,14 +10031,16 @@ World.integrateBody = function(body,dt){
* @param {Number} aj
* @param {Number} mu
*/
-World.prototype.runNarrowphase = function(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitution,surfaceVelocity){
+World.prototype.runNarrowphase = function(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitution,surfaceVelocity,glen){
if(!((si.collisionGroup & sj.collisionMask) !== 0 && (sj.collisionGroup & si.collisionMask) !== 0))
return;
+ /*
var reducedMass = bi.invMass + bj.invMass;
if(reducedMass > 0)
reducedMass = 1/reducedMass;
+ */
// Get world position and angle of each shape
vec2.rotate(xiw, xi, bi.angle);
@@ -9656,6 +10053,14 @@ World.prototype.runNarrowphase = function(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitut
// Run narrowphase
np.enableFriction = mu > 0;
np.frictionCoefficient = mu;
+ var reducedMass;
+ if(bi.motionState == Body.STATIC || bi.motionState == Body.KINEMATIC)
+ reducedMass = bj.mass;
+ else if(bj.motionState == Body.STATIC || bj.motionState == Body.KINEMATIC)
+ reducedMass = bi.mass;
+ else
+ reducedMass = (bi.mass*bj.mass)/(bi.mass+bj.mass);
+ np.slipForce = mu*glen*reducedMass;
np.restitution = restitution;
np.surfaceVelocity = surfaceVelocity;
@@ -9798,6 +10203,7 @@ World.prototype.getBodyById = function(id){
*
* @method toJSON
* @return {Object}
+ * @deprecated Should use Serializer instead.
*/
World.prototype.toJSON = function(){
var json = {
@@ -10250,7 +10656,7 @@ World.prototype.hitTest = function(worldPoint,bodies,precision){
return result;
};
-},{"../../package.json":8,"../collision/Broadphase":10,"../collision/NaiveBroadphase":12,"../collision/Narrowphase":13,"../constraints/DistanceConstraint":17,"../constraints/LockConstraint":19,"../constraints/PrismaticConstraint":20,"../constraints/RevoluteConstraint":21,"../events/EventEmitter":28,"../material/ContactMaterial":29,"../material/Material":30,"../math/vec2":33,"../objects/Body":34,"../objects/Spring":35,"../shapes/Capsule":37,"../shapes/Circle":38,"../shapes/Convex":39,"../shapes/Line":40,"../shapes/Particle":41,"../shapes/Plane":42,"../shapes/Rectangle":43,"../solver/GSSolver":45,"../utils/Utils":49}]},{},[36])
+},{"../../package.json":8,"../collision/Broadphase":10,"../collision/NaiveBroadphase":12,"../collision/Narrowphase":13,"../constraints/DistanceConstraint":17,"../constraints/LockConstraint":19,"../constraints/PrismaticConstraint":20,"../constraints/RevoluteConstraint":21,"../events/EventEmitter":28,"../material/ContactMaterial":29,"../material/Material":30,"../math/vec2":33,"../objects/Body":34,"../objects/Spring":35,"../shapes/Capsule":37,"../shapes/Circle":38,"../shapes/Convex":39,"../shapes/Line":41,"../shapes/Particle":42,"../shapes/Plane":43,"../shapes/Rectangle":44,"../solver/GSSolver":46,"../utils/Utils":50}]},{},[36])
(36)
});
;
\ No newline at end of file
diff --git a/examples/_site/examples.json b/examples/_site/examples.json
index b110cf63..7cec2fe0 100644
--- a/examples/_site/examples.json
+++ b/examples/_site/examples.json
@@ -772,6 +772,10 @@
"file": "bitmap+fonts.js",
"title": "bitmap fonts"
},
+ {
+ "file": "google+webfonts.js",
+ "title": "google webfonts"
+ },
{
"file": "hello+arial.js",
"title": "hello arial"