From 4a97861ff804275a21b178c13f3be3dcf54669e9 Mon Sep 17 00:00:00 2001 From: photonstorm Date: Wed, 26 Feb 2014 06:27:44 +0000 Subject: [PATCH] Updated p2 --- build/p2.js | 692 +++++++++++++++++++++++++++-------- examples/_site/examples.json | 4 + 2 files changed, 553 insertions(+), 143 deletions(-) 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"