cannon
Version:
A lightweight 3D physics engine written in JavaScript.
1,732 lines (1,502 loc) • 393 kB
JavaScript
/*
* Copyright (c) 2015 cannon.js Authors
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&false)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.CANNON=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
module.exports={
"name": "cannon",
"version": "0.6.2",
"description": "A lightweight 3D physics engine written in JavaScript.",
"homepage": "https://github.com/schteppe/cannon.js",
"author": "Stefan Hedman <schteppe@gmail.com> (http://steffe.se)",
"keywords": [
"cannon.js",
"cannon",
"physics",
"engine",
"3d"
],
"main": "./build/cannon.js",
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/schteppe/cannon.js.git"
},
"bugs": {
"url": "https://github.com/schteppe/cannon.js/issues"
},
"licenses": [
{
"type": "MIT"
}
],
"devDependencies": {
"jshint": "latest",
"uglify-js": "latest",
"nodeunit": "^0.9.0",
"grunt": "~0.4.0",
"grunt-contrib-jshint": "~0.1.1",
"grunt-contrib-nodeunit": "^0.4.1",
"grunt-contrib-concat": "~0.1.3",
"grunt-contrib-uglify": "^0.5.1",
"grunt-browserify": "^2.1.4",
"grunt-contrib-yuidoc": "^0.5.2",
"browserify": "*"
},
"dependencies": {}
}
},{}],2:[function(_dereq_,module,exports){
// Export classes
module.exports = {
version : _dereq_('../package.json').version,
AABB : _dereq_('./collision/AABB'),
ArrayCollisionMatrix : _dereq_('./collision/ArrayCollisionMatrix'),
Body : _dereq_('./objects/Body'),
Box : _dereq_('./shapes/Box'),
Broadphase : _dereq_('./collision/Broadphase'),
Constraint : _dereq_('./constraints/Constraint'),
ContactEquation : _dereq_('./equations/ContactEquation'),
Narrowphase : _dereq_('./world/Narrowphase'),
ConeTwistConstraint : _dereq_('./constraints/ConeTwistConstraint'),
ContactMaterial : _dereq_('./material/ContactMaterial'),
ConvexPolyhedron : _dereq_('./shapes/ConvexPolyhedron'),
Cylinder : _dereq_('./shapes/Cylinder'),
DistanceConstraint : _dereq_('./constraints/DistanceConstraint'),
Equation : _dereq_('./equations/Equation'),
EventTarget : _dereq_('./utils/EventTarget'),
FrictionEquation : _dereq_('./equations/FrictionEquation'),
GSSolver : _dereq_('./solver/GSSolver'),
GridBroadphase : _dereq_('./collision/GridBroadphase'),
Heightfield : _dereq_('./shapes/Heightfield'),
HingeConstraint : _dereq_('./constraints/HingeConstraint'),
LockConstraint : _dereq_('./constraints/LockConstraint'),
Mat3 : _dereq_('./math/Mat3'),
Material : _dereq_('./material/Material'),
NaiveBroadphase : _dereq_('./collision/NaiveBroadphase'),
ObjectCollisionMatrix : _dereq_('./collision/ObjectCollisionMatrix'),
Pool : _dereq_('./utils/Pool'),
Particle : _dereq_('./shapes/Particle'),
Plane : _dereq_('./shapes/Plane'),
PointToPointConstraint : _dereq_('./constraints/PointToPointConstraint'),
Quaternion : _dereq_('./math/Quaternion'),
Ray : _dereq_('./collision/Ray'),
RaycastVehicle : _dereq_('./objects/RaycastVehicle'),
RaycastResult : _dereq_('./collision/RaycastResult'),
RigidVehicle : _dereq_('./objects/RigidVehicle'),
RotationalEquation : _dereq_('./equations/RotationalEquation'),
RotationalMotorEquation : _dereq_('./equations/RotationalMotorEquation'),
SAPBroadphase : _dereq_('./collision/SAPBroadphase'),
SPHSystem : _dereq_('./objects/SPHSystem'),
Shape : _dereq_('./shapes/Shape'),
Solver : _dereq_('./solver/Solver'),
Sphere : _dereq_('./shapes/Sphere'),
SplitSolver : _dereq_('./solver/SplitSolver'),
Spring : _dereq_('./objects/Spring'),
Trimesh : _dereq_('./shapes/Trimesh'),
Vec3 : _dereq_('./math/Vec3'),
Vec3Pool : _dereq_('./utils/Vec3Pool'),
World : _dereq_('./world/World'),
};
},{"../package.json":1,"./collision/AABB":3,"./collision/ArrayCollisionMatrix":4,"./collision/Broadphase":5,"./collision/GridBroadphase":6,"./collision/NaiveBroadphase":7,"./collision/ObjectCollisionMatrix":8,"./collision/Ray":9,"./collision/RaycastResult":10,"./collision/SAPBroadphase":11,"./constraints/ConeTwistConstraint":12,"./constraints/Constraint":13,"./constraints/DistanceConstraint":14,"./constraints/HingeConstraint":15,"./constraints/LockConstraint":16,"./constraints/PointToPointConstraint":17,"./equations/ContactEquation":19,"./equations/Equation":20,"./equations/FrictionEquation":21,"./equations/RotationalEquation":22,"./equations/RotationalMotorEquation":23,"./material/ContactMaterial":24,"./material/Material":25,"./math/Mat3":27,"./math/Quaternion":28,"./math/Vec3":30,"./objects/Body":31,"./objects/RaycastVehicle":32,"./objects/RigidVehicle":33,"./objects/SPHSystem":34,"./objects/Spring":35,"./shapes/Box":37,"./shapes/ConvexPolyhedron":38,"./shapes/Cylinder":39,"./shapes/Heightfield":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Shape":43,"./shapes/Sphere":44,"./shapes/Trimesh":45,"./solver/GSSolver":46,"./solver/Solver":47,"./solver/SplitSolver":48,"./utils/EventTarget":49,"./utils/Pool":51,"./utils/Vec3Pool":54,"./world/Narrowphase":55,"./world/World":56}],3:[function(_dereq_,module,exports){
var Vec3 = _dereq_('../math/Vec3');
var Utils = _dereq_('../utils/Utils');
module.exports = AABB;
/**
* Axis aligned bounding box class.
* @class AABB
* @constructor
* @param {Object} [options]
* @param {Vec3} [options.upperBound]
* @param {Vec3} [options.lowerBound]
*/
function AABB(options){
options = options || {};
/**
* The lower bound of the bounding box.
* @property lowerBound
* @type {Vec3}
*/
this.lowerBound = new Vec3();
if(options.lowerBound){
this.lowerBound.copy(options.lowerBound);
}
/**
* The upper bound of the bounding box.
* @property upperBound
* @type {Vec3}
*/
this.upperBound = new Vec3();
if(options.upperBound){
this.upperBound.copy(options.upperBound);
}
}
var tmp = new Vec3();
/**
* Set the AABB bounds from a set of points.
* @method setFromPoints
* @param {Array} points An array of Vec3's.
* @param {Vec3} position
* @param {Quaternion} quaternion
* @param {number} skinSize
* @return {AABB} The self object
*/
AABB.prototype.setFromPoints = function(points, position, quaternion, skinSize){
var l = this.lowerBound,
u = this.upperBound,
q = quaternion;
// Set to the first point
l.copy(points[0]);
if(q){
q.vmult(l, l);
}
u.copy(l);
for(var i = 1; i<points.length; i++){
var p = points[i];
if(q){
q.vmult(p, tmp);
p = tmp;
}
if(p.x > u.x){ u.x = p.x; }
if(p.x < l.x){ l.x = p.x; }
if(p.y > u.y){ u.y = p.y; }
if(p.y < l.y){ l.y = p.y; }
if(p.z > u.z){ u.z = p.z; }
if(p.z < l.z){ l.z = p.z; }
}
// Add offset
if (position) {
position.vadd(l, l);
position.vadd(u, u);
}
if(skinSize){
l.x -= skinSize;
l.y -= skinSize;
l.z -= skinSize;
u.x += skinSize;
u.y += skinSize;
u.z += skinSize;
}
return this;
};
/**
* Copy bounds from an AABB to this AABB
* @method copy
* @param {AABB} aabb Source to copy from
* @return {AABB} The this object, for chainability
*/
AABB.prototype.copy = function(aabb){
this.lowerBound.copy(aabb.lowerBound);
this.upperBound.copy(aabb.upperBound);
return this;
};
/**
* Clone an AABB
* @method clone
*/
AABB.prototype.clone = function(){
return new AABB().copy(this);
};
/**
* Extend this AABB so that it covers the given AABB too.
* @method extend
* @param {AABB} aabb
*/
AABB.prototype.extend = function(aabb){
// Extend lower bound
var l = aabb.lowerBound.x;
if(this.lowerBound.x > l){
this.lowerBound.x = l;
}
// Upper
var u = aabb.upperBound.x;
if(this.upperBound.x < u){
this.upperBound.x = u;
}
// Extend lower bound
var l = aabb.lowerBound.y;
if(this.lowerBound.y > l){
this.lowerBound.y = l;
}
// Upper
var u = aabb.upperBound.y;
if(this.upperBound.y < u){
this.upperBound.y = u;
}
// Extend lower bound
var l = aabb.lowerBound.z;
if(this.lowerBound.z > l){
this.lowerBound.z = l;
}
// Upper
var u = aabb.upperBound.z;
if(this.upperBound.z < u){
this.upperBound.z = u;
}
};
/**
* Returns true if the given AABB overlaps this AABB.
* @method overlaps
* @param {AABB} aabb
* @return {Boolean}
*/
AABB.prototype.overlaps = function(aabb){
var l1 = this.lowerBound,
u1 = this.upperBound,
l2 = aabb.lowerBound,
u2 = aabb.upperBound;
// l2 u2
// |---------|
// |--------|
// l1 u1
return ((l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x)) &&
((l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y)) &&
((l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z));
};
/**
* Returns true if the given AABB is fully contained in this AABB.
* @method contains
* @param {AABB} aabb
* @return {Boolean}
*/
AABB.prototype.contains = function(aabb){
var l1 = this.lowerBound,
u1 = this.upperBound,
l2 = aabb.lowerBound,
u2 = aabb.upperBound;
// l2 u2
// |---------|
// |---------------|
// l1 u1
return (
(l1.x <= l2.x && u1.x >= u2.x) &&
(l1.y <= l2.y && u1.y >= u2.y) &&
(l1.z <= l2.z && u1.z >= u2.z)
);
};
/**
* @method getCorners
* @param {Vec3} a
* @param {Vec3} b
* @param {Vec3} c
* @param {Vec3} d
* @param {Vec3} e
* @param {Vec3} f
* @param {Vec3} g
* @param {Vec3} h
*/
AABB.prototype.getCorners = function(a, b, c, d, e, f, g, h){
var l = this.lowerBound,
u = this.upperBound;
a.copy(l);
b.set( u.x, l.y, l.z );
c.set( u.x, u.y, l.z );
d.set( l.x, u.y, u.z );
e.set( u.x, l.y, l.z );
f.set( l.x, u.y, l.z );
g.set( l.x, l.y, u.z );
h.copy(u);
};
var transformIntoFrame_corners = [
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3()
];
/**
* Get the representation of an AABB in another frame.
* @method toLocalFrame
* @param {Transform} frame
* @param {AABB} target
* @return {AABB} The "target" AABB object.
*/
AABB.prototype.toLocalFrame = function(frame, target){
var corners = transformIntoFrame_corners;
var a = corners[0];
var b = corners[1];
var c = corners[2];
var d = corners[3];
var e = corners[4];
var f = corners[5];
var g = corners[6];
var h = corners[7];
// Get corners in current frame
this.getCorners(a, b, c, d, e, f, g, h);
// Transform them to new local frame
for(var i=0; i !== 8; i++){
var corner = corners[i];
frame.pointToLocal(corner, corner);
}
return target.setFromPoints(corners);
};
/**
* Get the representation of an AABB in the global frame.
* @method toWorldFrame
* @param {Transform} frame
* @param {AABB} target
* @return {AABB} The "target" AABB object.
*/
AABB.prototype.toWorldFrame = function(frame, target){
var corners = transformIntoFrame_corners;
var a = corners[0];
var b = corners[1];
var c = corners[2];
var d = corners[3];
var e = corners[4];
var f = corners[5];
var g = corners[6];
var h = corners[7];
// Get corners in current frame
this.getCorners(a, b, c, d, e, f, g, h);
// Transform them to new local frame
for(var i=0; i !== 8; i++){
var corner = corners[i];
frame.pointToWorld(corner, corner);
}
return target.setFromPoints(corners);
};
},{"../math/Vec3":30,"../utils/Utils":53}],4:[function(_dereq_,module,exports){
module.exports = ArrayCollisionMatrix;
/**
* Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step
* @class ArrayCollisionMatrix
* @constructor
*/
function ArrayCollisionMatrix() {
/**
* The matrix storage
* @property matrix
* @type {Array}
*/
this.matrix = [];
}
/**
* Get an element
* @method get
* @param {Number} i
* @param {Number} j
* @return {Number}
*/
ArrayCollisionMatrix.prototype.get = function(i, j) {
i = i.index;
j = j.index;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
return this.matrix[(i*(i + 1)>>1) + j-1];
};
/**
* Set an element
* @method set
* @param {Number} i
* @param {Number} j
* @param {Number} value
*/
ArrayCollisionMatrix.prototype.set = function(i, j, value) {
i = i.index;
j = j.index;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
this.matrix[(i*(i + 1)>>1) + j-1] = value ? 1 : 0;
};
/**
* Sets all elements to zero
* @method reset
*/
ArrayCollisionMatrix.prototype.reset = function() {
for (var i=0, l=this.matrix.length; i!==l; i++) {
this.matrix[i]=0;
}
};
/**
* Sets the max number of objects
* @method setNumObjects
* @param {Number} n
*/
ArrayCollisionMatrix.prototype.setNumObjects = function(n) {
this.matrix.length = n*(n-1)>>1;
};
},{}],5:[function(_dereq_,module,exports){
var Body = _dereq_('../objects/Body');
var Vec3 = _dereq_('../math/Vec3');
var Quaternion = _dereq_('../math/Quaternion');
var Shape = _dereq_('../shapes/Shape');
var Plane = _dereq_('../shapes/Plane');
module.exports = Broadphase;
/**
* Base class for broadphase implementations
* @class Broadphase
* @constructor
* @author schteppe
*/
function Broadphase(){
/**
* The world to search for collisions in.
* @property world
* @type {World}
*/
this.world = null;
/**
* If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.
* @property useBoundingBoxes
* @type {Boolean}
*/
this.useBoundingBoxes = false;
/**
* Set to true if the objects in the world moved.
* @property {Boolean} dirty
*/
this.dirty = true;
}
/**
* Get the collision pairs from the world
* @method collisionPairs
* @param {World} world The world to search in
* @param {Array} p1 Empty array to be filled with body objects
* @param {Array} p2 Empty array to be filled with body objects
*/
Broadphase.prototype.collisionPairs = function(world,p1,p2){
throw new Error("collisionPairs not implemented for this BroadPhase class!");
};
/**
* Check if a body pair needs to be intersection tested at all.
* @method needBroadphaseCollision
* @param {Body} bodyA
* @param {Body} bodyB
* @return {bool}
*/
var Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC = Body.STATIC | Body.KINEMATIC;
Broadphase.prototype.needBroadphaseCollision = function(bodyA,bodyB){
// Check collision filter masks
if( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask)===0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask)===0){
return false;
}
// Check types
if(((bodyA.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyA.sleepState === Body.SLEEPING) &&
((bodyB.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyB.sleepState === Body.SLEEPING)) {
// Both bodies are static, kinematic or sleeping. Skip.
return false;
}
return true;
};
/**
* Check if the bounding volumes of two bodies intersect.
* @method intersectionTest
* @param {Body} bodyA
* @param {Body} bodyB
* @param {array} pairs1
* @param {array} pairs2
*/
Broadphase.prototype.intersectionTest = function(bodyA, bodyB, pairs1, pairs2){
if(this.useBoundingBoxes){
this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2);
} else {
this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2);
}
};
/**
* Check if the bounding spheres of two bodies are intersecting.
* @method doBoundingSphereBroadphase
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Array} pairs1 bodyA is appended to this array if intersection
* @param {Array} pairs2 bodyB is appended to this array if intersection
*/
var Broadphase_collisionPairs_r = new Vec3(), // Temp objects
Broadphase_collisionPairs_normal = new Vec3(),
Broadphase_collisionPairs_quat = new Quaternion(),
Broadphase_collisionPairs_relpos = new Vec3();
Broadphase.prototype.doBoundingSphereBroadphase = function(bodyA,bodyB,pairs1,pairs2){
var r = Broadphase_collisionPairs_r;
bodyB.position.vsub(bodyA.position,r);
var boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2);
var norm2 = r.norm2();
if(norm2 < boundingRadiusSum2){
pairs1.push(bodyA);
pairs2.push(bodyB);
}
};
/**
* Check if the bounding boxes of two bodies are intersecting.
* @method doBoundingBoxBroadphase
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Array} pairs1
* @param {Array} pairs2
*/
Broadphase.prototype.doBoundingBoxBroadphase = function(bodyA,bodyB,pairs1,pairs2){
if(bodyA.aabbNeedsUpdate){
bodyA.computeAABB();
}
if(bodyB.aabbNeedsUpdate){
bodyB.computeAABB();
}
// Check AABB / AABB
if(bodyA.aabb.overlaps(bodyB.aabb)){
pairs1.push(bodyA);
pairs2.push(bodyB);
}
};
/**
* Removes duplicate pairs from the pair arrays.
* @method makePairsUnique
* @param {Array} pairs1
* @param {Array} pairs2
*/
var Broadphase_makePairsUnique_temp = { keys:[] },
Broadphase_makePairsUnique_p1 = [],
Broadphase_makePairsUnique_p2 = [];
Broadphase.prototype.makePairsUnique = function(pairs1,pairs2){
var t = Broadphase_makePairsUnique_temp,
p1 = Broadphase_makePairsUnique_p1,
p2 = Broadphase_makePairsUnique_p2,
N = pairs1.length;
for(var i=0; i!==N; i++){
p1[i] = pairs1[i];
p2[i] = pairs2[i];
}
pairs1.length = 0;
pairs2.length = 0;
for(var i=0; i!==N; i++){
var id1 = p1[i].id,
id2 = p2[i].id;
var key = id1 < id2 ? id1+","+id2 : id2+","+id1;
t[key] = i;
t.keys.push(key);
}
for(var i=0; i!==t.keys.length; i++){
var key = t.keys.pop(),
pairIndex = t[key];
pairs1.push(p1[pairIndex]);
pairs2.push(p2[pairIndex]);
delete t[key];
}
};
/**
* To be implemented by subcasses
* @method setWorld
* @param {World} world
*/
Broadphase.prototype.setWorld = function(world){
};
/**
* Check if the bounding spheres of two bodies overlap.
* @method boundingSphereCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {boolean}
*/
var bsc_dist = new Vec3();
Broadphase.boundingSphereCheck = function(bodyA,bodyB){
var dist = bsc_dist;
bodyA.position.vsub(bodyB.position,dist);
return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius,2) > dist.norm2();
};
/**
* Returns all the bodies within the AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
Broadphase.prototype.aabbQuery = function(world, aabb, result){
console.warn('.aabbQuery is not implemented in this Broadphase subclass.');
return [];
};
},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Plane":42,"../shapes/Shape":43}],6:[function(_dereq_,module,exports){
module.exports = GridBroadphase;
var Broadphase = _dereq_('./Broadphase');
var Vec3 = _dereq_('../math/Vec3');
var Shape = _dereq_('../shapes/Shape');
/**
* Axis aligned uniform grid broadphase.
* @class GridBroadphase
* @constructor
* @extends Broadphase
* @todo Needs support for more than just planes and spheres.
* @param {Vec3} aabbMin
* @param {Vec3} aabbMax
* @param {Number} nx Number of boxes along x
* @param {Number} ny Number of boxes along y
* @param {Number} nz Number of boxes along z
*/
function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){
Broadphase.apply(this);
this.nx = nx || 10;
this.ny = ny || 10;
this.nz = nz || 10;
this.aabbMin = aabbMin || new Vec3(100,100,100);
this.aabbMax = aabbMax || new Vec3(-100,-100,-100);
var nbins = this.nx * this.ny * this.nz;
if (nbins <= 0) {
throw "GridBroadphase: Each dimension's n must be >0";
}
this.bins = [];
this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow
this.bins.length = nbins;
this.binLengths.length = nbins;
for (var i=0;i<nbins;i++) {
this.bins[i]=[];
this.binLengths[i]=0;
}
}
GridBroadphase.prototype = new Broadphase();
GridBroadphase.prototype.constructor = GridBroadphase;
/**
* Get all the collision pairs in the physics world
* @method collisionPairs
* @param {World} world
* @param {Array} pairs1
* @param {Array} pairs2
*/
var GridBroadphase_collisionPairs_d = new Vec3();
var GridBroadphase_collisionPairs_binPos = new Vec3();
GridBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){
var N = world.numObjects(),
bodies = world.bodies;
var max = this.aabbMax,
min = this.aabbMin,
nx = this.nx,
ny = this.ny,
nz = this.nz;
var xstep = ny*nz;
var ystep = nz;
var zstep = 1;
var xmax = max.x,
ymax = max.y,
zmax = max.z,
xmin = min.x,
ymin = min.y,
zmin = min.z;
var xmult = nx / (xmax-xmin),
ymult = ny / (ymax-ymin),
zmult = nz / (zmax-zmin);
var binsizeX = (xmax - xmin) / nx,
binsizeY = (ymax - ymin) / ny,
binsizeZ = (zmax - zmin) / nz;
var binRadius = Math.sqrt(binsizeX*binsizeX + binsizeY*binsizeY + binsizeZ*binsizeZ) * 0.5;
var types = Shape.types;
var SPHERE = types.SPHERE,
PLANE = types.PLANE,
BOX = types.BOX,
COMPOUND = types.COMPOUND,
CONVEXPOLYHEDRON = types.CONVEXPOLYHEDRON;
var bins=this.bins,
binLengths=this.binLengths,
Nbins=this.bins.length;
// Reset bins
for(var i=0; i!==Nbins; i++){
binLengths[i] = 0;
}
var ceil = Math.ceil;
var min = Math.min;
var max = Math.max;
function addBoxToBins(x0,y0,z0,x1,y1,z1,bi) {
var xoff0 = ((x0 - xmin) * xmult)|0,
yoff0 = ((y0 - ymin) * ymult)|0,
zoff0 = ((z0 - zmin) * zmult)|0,
xoff1 = ceil((x1 - xmin) * xmult),
yoff1 = ceil((y1 - ymin) * ymult),
zoff1 = ceil((z1 - zmin) * zmult);
if (xoff0 < 0) { xoff0 = 0; } else if (xoff0 >= nx) { xoff0 = nx - 1; }
if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; }
if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; }
if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; }
if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; }
if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; }
xoff0 *= xstep;
yoff0 *= ystep;
zoff0 *= zstep;
xoff1 *= xstep;
yoff1 *= ystep;
zoff1 *= zstep;
for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) {
for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) {
for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) {
var idx = xoff+yoff+zoff;
bins[idx][binLengths[idx]++] = bi;
}
}
}
}
// Put all bodies into the bins
for(var i=0; i!==N; i++){
var bi = bodies[i];
var si = bi.shape;
switch(si.type){
case SPHERE:
// Put in bin
// check if overlap with other bins
var x = bi.position.x,
y = bi.position.y,
z = bi.position.z;
var r = si.radius;
addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi);
break;
case PLANE:
if(si.worldNormalNeedsUpdate){
si.computeWorldNormal(bi.quaternion);
}
var planeNormal = si.worldNormal;
//Relative position from origin of plane object to the first bin
//Incremented as we iterate through the bins
var xreset = xmin + binsizeX*0.5 - bi.position.x,
yreset = ymin + binsizeY*0.5 - bi.position.y,
zreset = zmin + binsizeZ*0.5 - bi.position.z;
var d = GridBroadphase_collisionPairs_d;
d.set(xreset, yreset, zreset);
for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) {
for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) {
for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) {
if (d.dot(planeNormal) < binRadius) {
var idx = xoff + yoff + zoff;
bins[idx][binLengths[idx]++] = bi;
}
}
}
}
break;
default:
if (bi.aabbNeedsUpdate) {
bi.computeAABB();
}
addBoxToBins(
bi.aabb.lowerBound.x,
bi.aabb.lowerBound.y,
bi.aabb.lowerBound.z,
bi.aabb.upperBound.x,
bi.aabb.upperBound.y,
bi.aabb.upperBound.z,
bi);
break;
}
}
// Check each bin
for(var i=0; i!==Nbins; i++){
var binLength = binLengths[i];
//Skip bins with no potential collisions
if (binLength > 1) {
var bin = bins[i];
// Do N^2 broadphase inside
for(var xi=0; xi!==binLength; xi++){
var bi = bin[xi];
for(var yi=0; yi!==xi; yi++){
var bj = bin[yi];
if(this.needBroadphaseCollision(bi,bj)){
this.intersectionTest(bi,bj,pairs1,pairs2);
}
}
}
}
}
// for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) {
// console.log("layer "+zi);
// for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) {
// var row = '';
// for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) {
// var idx = xoff + yoff + zoff;
// row += ' ' + binLengths[idx];
// }
// console.log(row);
// }
// }
this.makePairsUnique(pairs1,pairs2);
};
},{"../math/Vec3":30,"../shapes/Shape":43,"./Broadphase":5}],7:[function(_dereq_,module,exports){
module.exports = NaiveBroadphase;
var Broadphase = _dereq_('./Broadphase');
var AABB = _dereq_('./AABB');
/**
* Naive broadphase implementation, used in lack of better ones.
* @class NaiveBroadphase
* @constructor
* @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad)
* @extends Broadphase
*/
function NaiveBroadphase(){
Broadphase.apply(this);
}
NaiveBroadphase.prototype = new Broadphase();
NaiveBroadphase.prototype.constructor = NaiveBroadphase;
/**
* Get all the collision pairs in the physics world
* @method collisionPairs
* @param {World} world
* @param {Array} pairs1
* @param {Array} pairs2
*/
NaiveBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){
var bodies = world.bodies,
n = bodies.length,
i,j,bi,bj;
// Naive N^2 ftw!
for(i=0; i!==n; i++){
for(j=0; j!==i; j++){
bi = bodies[i];
bj = bodies[j];
if(!this.needBroadphaseCollision(bi,bj)){
continue;
}
this.intersectionTest(bi,bj,pairs1,pairs2);
}
}
};
var tmpAABB = new AABB();
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){
result = result || [];
for(var i = 0; i < world.bodies.length; i++){
var b = world.bodies[i];
if(b.aabbNeedsUpdate){
b.computeAABB();
}
// Ugly hack until Body gets aabb
if(b.aabb.overlaps(aabb)){
result.push(b);
}
}
return result;
};
},{"./AABB":3,"./Broadphase":5}],8:[function(_dereq_,module,exports){
module.exports = ObjectCollisionMatrix;
/**
* Records what objects are colliding with each other
* @class ObjectCollisionMatrix
* @constructor
*/
function ObjectCollisionMatrix() {
/**
* The matrix storage
* @property matrix
* @type {Object}
*/
this.matrix = {};
}
/**
* @method get
* @param {Number} i
* @param {Number} j
* @return {Number}
*/
ObjectCollisionMatrix.prototype.get = function(i, j) {
i = i.id;
j = j.id;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
return i+'-'+j in this.matrix;
};
/**
* @method set
* @param {Number} i
* @param {Number} j
* @param {Number} value
*/
ObjectCollisionMatrix.prototype.set = function(i, j, value) {
i = i.id;
j = j.id;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
if (value) {
this.matrix[i+'-'+j] = true;
}
else {
delete this.matrix[i+'-'+j];
}
};
/**
* Empty the matrix
* @method reset
*/
ObjectCollisionMatrix.prototype.reset = function() {
this.matrix = {};
};
/**
* Set max number of objects
* @method setNumObjects
* @param {Number} n
*/
ObjectCollisionMatrix.prototype.setNumObjects = function(n) {
};
},{}],9:[function(_dereq_,module,exports){
module.exports = Ray;
var Vec3 = _dereq_('../math/Vec3');
var Quaternion = _dereq_('../math/Quaternion');
var Transform = _dereq_('../math/Transform');
var ConvexPolyhedron = _dereq_('../shapes/ConvexPolyhedron');
var Box = _dereq_('../shapes/Box');
var RaycastResult = _dereq_('../collision/RaycastResult');
var Shape = _dereq_('../shapes/Shape');
var AABB = _dereq_('../collision/AABB');
/**
* A line in 3D space that intersects bodies and return points.
* @class Ray
* @constructor
* @param {Vec3} from
* @param {Vec3} to
*/
function Ray(from, to){
/**
* @property {Vec3} from
*/
this.from = from ? from.clone() : new Vec3();
/**
* @property {Vec3} to
*/
this.to = to ? to.clone() : new Vec3();
/**
* @private
* @property {Vec3} _direction
*/
this._direction = new Vec3();
/**
* The precision of the ray. Used when checking parallelity etc.
* @property {Number} precision
*/
this.precision = 0.0001;
/**
* Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.
* @property {Boolean} checkCollisionResponse
*/
this.checkCollisionResponse = true;
/**
* If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.
* @property {Boolean} skipBackfaces
*/
this.skipBackfaces = false;
/**
* @property {number} collisionFilterMask
* @default -1
*/
this.collisionFilterMask = -1;
/**
* @property {number} collisionFilterGroup
* @default -1
*/
this.collisionFilterGroup = -1;
/**
* The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST.
* @property {number} mode
*/
this.mode = Ray.ANY;
/**
* Current result object.
* @property {RaycastResult} result
*/
this.result = new RaycastResult();
/**
* Will be set to true during intersectWorld() if the ray hit anything.
* @property {Boolean} hasHit
*/
this.hasHit = false;
/**
* Current, user-provided result callback. Will be used if mode is Ray.ALL.
* @property {Function} callback
*/
this.callback = function(result){};
}
Ray.prototype.constructor = Ray;
Ray.CLOSEST = 1;
Ray.ANY = 2;
Ray.ALL = 4;
var tmpAABB = new AABB();
var tmpArray = [];
/**
* Do itersection against all bodies in the given World.
* @method intersectWorld
* @param {World} world
* @param {object} options
* @return {Boolean} True if the ray hit anything, otherwise false.
*/
Ray.prototype.intersectWorld = function (world, options) {
this.mode = options.mode || Ray.ANY;
this.result = options.result || new RaycastResult();
this.skipBackfaces = !!options.skipBackfaces;
this.collisionFilterMask = typeof(options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1;
this.collisionFilterGroup = typeof(options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1;
if(options.from){
this.from.copy(options.from);
}
if(options.to){
this.to.copy(options.to);
}
this.callback = options.callback || function(){};
this.hasHit = false;
this.result.reset();
this._updateDirection();
this.getAABB(tmpAABB);
tmpArray.length = 0;
world.broadphase.aabbQuery(world, tmpAABB, tmpArray);
this.intersectBodies(tmpArray);
return this.hasHit;
};
var v1 = new Vec3(),
v2 = new Vec3();
/*
* As per "Barycentric Technique" as named here http://www.blackpawn.com/texts/pointinpoly/default.html But without the division
*/
Ray.pointInTriangle = pointInTriangle;
function pointInTriangle(p, a, b, c) {
c.vsub(a,v0);
b.vsub(a,v1);
p.vsub(a,v2);
var dot00 = v0.dot( v0 );
var dot01 = v0.dot( v1 );
var dot02 = v0.dot( v2 );
var dot11 = v1.dot( v1 );
var dot12 = v1.dot( v2 );
var u,v;
return ( (u = dot11 * dot02 - dot01 * dot12) >= 0 ) &&
( (v = dot00 * dot12 - dot01 * dot02) >= 0 ) &&
( u + v < ( dot00 * dot11 - dot01 * dot01 ) );
}
/**
* Shoot a ray at a body, get back information about the hit.
* @method intersectBody
* @private
* @param {Body} body
* @param {RaycastResult} [result] Deprecated - set the result property of the Ray instead.
*/
var intersectBody_xi = new Vec3();
var intersectBody_qi = new Quaternion();
Ray.prototype.intersectBody = function (body, result) {
if(result){
this.result = result;
this._updateDirection();
}
var checkCollisionResponse = this.checkCollisionResponse;
if(checkCollisionResponse && !body.collisionResponse){
return;
}
if((this.collisionFilterGroup & body.collisionFilterMask)===0 || (body.collisionFilterGroup & this.collisionFilterMask)===0){
return;
}
var xi = intersectBody_xi;
var qi = intersectBody_qi;
for (var i = 0, N = body.shapes.length; i < N; i++) {
var shape = body.shapes[i];
if(checkCollisionResponse && !shape.collisionResponse){
continue; // Skip
}
body.quaternion.mult(body.shapeOrientations[i], qi);
body.quaternion.vmult(body.shapeOffsets[i], xi);
xi.vadd(body.position, xi);
this.intersectShape(
shape,
qi,
xi,
body
);
if(this.result._shouldStop){
break;
}
}
};
/**
* @method intersectBodies
* @param {Array} bodies An array of Body objects.
* @param {RaycastResult} [result] Deprecated
*/
Ray.prototype.intersectBodies = function (bodies, result) {
if(result){
this.result = result;
this._updateDirection();
}
for ( var i = 0, l = bodies.length; !this.result._shouldStop && i < l; i ++ ) {
this.intersectBody(bodies[i]);
}
};
/**
* Updates the _direction vector.
* @private
* @method _updateDirection
*/
Ray.prototype._updateDirection = function(){
this.to.vsub(this.from, this._direction);
this._direction.normalize();
};
/**
* @method intersectShape
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectShape = function(shape, quat, position, body){
var from = this.from;
// Checking boundingSphere
var distance = distanceFromIntersection(from, this._direction, position);
if ( distance > shape.boundingSphereRadius ) {
return;
}
var intersectMethod = this[shape.type];
if(intersectMethod){
intersectMethod.call(this, shape, quat, position, body);
}
};
var vector = new Vec3();
var normal = new Vec3();
var intersectPoint = new Vec3();
var a = new Vec3();
var b = new Vec3();
var c = new Vec3();
var d = new Vec3();
var tmpRaycastResult = new RaycastResult();
/**
* @method intersectBox
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectBox = function(shape, quat, position, body){
return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body);
};
Ray.prototype[Shape.types.BOX] = Ray.prototype.intersectBox;
/**
* @method intersectPlane
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectPlane = function(shape, quat, position, body){
var from = this.from;
var to = this.to;
var direction = this._direction;
// Get plane normal
var worldNormal = new Vec3(0, 0, 1);
quat.vmult(worldNormal, worldNormal);
var len = new Vec3();
from.vsub(position, len);
var planeToFrom = len.dot(worldNormal);
to.vsub(position, len);
var planeToTo = len.dot(worldNormal);
if(planeToFrom * planeToTo > 0){
// "from" and "to" are on the same side of the plane... bail out
return;
}
if(from.distanceTo(to) < planeToFrom){
return;
}
var n_dot_dir = worldNormal.dot(direction);
if (Math.abs(n_dot_dir) < this.precision) {
// No intersection
return;
}
var planePointToFrom = new Vec3();
var dir_scaled_with_t = new Vec3();
var hitPointWorld = new Vec3();
from.vsub(position, planePointToFrom);
var t = -worldNormal.dot(planePointToFrom) / n_dot_dir;
direction.scale(t, dir_scaled_with_t);
from.vadd(dir_scaled_with_t, hitPointWorld);
this.reportIntersection(worldNormal, hitPointWorld, shape, body, -1);
};
Ray.prototype[Shape.types.PLANE] = Ray.prototype.intersectPlane;
/**
* Get the world AABB of the ray.
* @method getAABB
* @param {AABB} aabb
*/
Ray.prototype.getAABB = function(result){
var to = this.to;
var from = this.from;
result.lowerBound.x = Math.min(to.x, from.x);
result.lowerBound.y = Math.min(to.y, from.y);
result.lowerBound.z = Math.min(to.z, from.z);
result.upperBound.x = Math.max(to.x, from.x);
result.upperBound.y = Math.max(to.y, from.y);
result.upperBound.z = Math.max(to.z, from.z);
};
var intersectConvexOptions = {
faceList: [0]
};
/**
* @method intersectHeightfield
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectHeightfield = function(shape, quat, position, body){
var data = shape.data,
w = shape.elementSize,
worldPillarOffset = new Vec3();
// Convert the ray to local heightfield coordinates
var localRay = new Ray(this.from, this.to);
Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from);
Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to);
// Get the index of the data points to test against
var index = [];
var iMinX = null;
var iMinY = null;
var iMaxX = null;
var iMaxY = null;
var inside = shape.getIndexOfPosition(localRay.from.x, localRay.from.y, index, false);
if(inside){
iMinX = index[0];
iMinY = index[1];
iMaxX = index[0];
iMaxY = index[1];
}
inside = shape.getIndexOfPosition(localRay.to.x, localRay.to.y, index, false);
if(inside){
if (iMinX === null || index[0] < iMinX) { iMinX = index[0]; }
if (iMaxX === null || index[0] > iMaxX) { iMaxX = index[0]; }
if (iMinY === null || index[1] < iMinY) { iMinY = index[1]; }
if (iMaxY === null || index[1] > iMaxY) { iMaxY = index[1]; }
}
if(iMinX === null){
return;
}
var minMax = [];
shape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax);
var min = minMax[0];
var max = minMax[1];
// // Bail out if the ray can't touch the bounding box
// // TODO
// var aabb = new AABB();
// this.getAABB(aabb);
// if(aabb.intersects()){
// return;
// }
for(var i = iMinX; i <= iMaxX; i++){
for(var j = iMinY; j <= iMaxY; j++){
if(this.result._shouldStop){
return;
}
// Lower triangle
shape.getConvexTrianglePillar(i, j, false);
Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset);
this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions);
if(this.result._shouldStop){
return;
}
// Upper triangle
shape.getConvexTrianglePillar(i, j, true);
Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset);
this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions);
}
}
};
Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype.intersectHeightfield;
var Ray_intersectSphere_intersectionPoint = new Vec3();
var Ray_intersectSphere_normal = new Vec3();
/**
* @method intersectSphere
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectSphere = function(shape, quat, position, body){
var from = this.from,
to = this.to,
r = shape.radius;
var a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2);
var b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z));
var c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2);
var delta = Math.pow(b, 2) - 4 * a * c;
var intersectionPoint = Ray_intersectSphere_intersectionPoint;
var normal = Ray_intersectSphere_normal;
if(delta < 0){
// No intersection
return;
} else if(delta === 0){
// single intersection point
from.lerp(to, delta, intersectionPoint);
intersectionPoint.vsub(position, normal);
normal.normalize();
this.reportIntersection(normal, intersectionPoint, shape, body, -1);
} else {
var d1 = (- b - Math.sqrt(delta)) / (2 * a);
var d2 = (- b + Math.sqrt(delta)) / (2 * a);
if(d1 >= 0 && d1 <= 1){
from.lerp(to, d1, intersectionPoint);
intersectionPoint.vsub(position, normal);
normal.normalize();
this.reportIntersection(normal, intersectionPoint, shape, body, -1);
}
if(this.result._shouldStop){
return;
}
if(d2 >= 0 && d2 <= 1){
from.lerp(to, d2, intersectionPoint);
intersectionPoint.vsub(position, normal);
normal.normalize();
this.reportIntersection(normal, intersectionPoint, shape, body, -1);
}
}
};
Ray.prototype[Shape.types.SPHERE] = Ray.prototype.intersectSphere;
var intersectConvex_normal = new Vec3();
var intersectConvex_minDistNormal = new Vec3();
var intersectConvex_minDistIntersect = new Vec3();
var intersectConvex_vector = new Vec3();
/**
* @method intersectConvex
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
* @param {object} [options]
* @param {array} [options.faceList]
*/
Ray.prototype.intersectConvex = function intersectConvex(
shape,
quat,
position,
body,
options
){
var minDistNormal = intersectConvex_minDistNormal;
var normal = intersectConvex_normal;
var vector = intersectConvex_vector;
var minDistIntersect = intersectConvex_minDistIntersect;
var faceList = (options && options.faceList) || null;
// Checking faces
var faces = shape.faces,
vertices = shape.vertices,
normals = shape.faceNormals;
var direction = this._direction;
var from = this.from;
var to = this.to;
var fromToDistance = from.distanceTo(to);
var minDist = -1;
var Nfaces = faceList ? faceList.length : faces.length;
var result = this.result;
for (var j = 0; !result._shouldStop && j < Nfaces; j++) {
var fi = faceList ? faceList[j] : j;
var face = faces[fi];
var faceNormal = normals[fi];
var q = quat;
var x = position;
// determine if ray intersects the plane of the face
// note: this works regardless of the direction of the face normal
// Get plane point in world coordinates...
vector.copy(vertices[face[0]]);
q.vmult(vector,vector);
vector.vadd(x,vector);
// ...but make it relative to the ray from. We'll fix this later.
vector.vsub(from,vector);
// Get plane normal
q.vmult(faceNormal,normal);
// If this dot product is negative, we have something interesting
var dot = direction.dot(normal);
// Bail out if ray and plane are parallel
if ( Math.abs( dot ) < this.precision ){
continue;
}
// calc distance to plane
var scalar = normal.dot(vector) / dot;
// if negative distance, then plane is behind ray
if (scalar < 0){
continue;
}
// if (dot < 0) {
// Intersection point is from + direction * scalar
direction.mult(scalar,intersectPoint);
intersectPoint.vadd(from,intersectPoint);
// a is the point we compare points b and c with.
a.copy(vertices[face[0]]);
q.vmult(a,a);
x.vadd(a,a);
for(var i = 1; !result._shouldStop && i < face.length - 1; i++){
// Transform 3 vertices to world coords
b.copy(vertices[face[i]]);
c.copy(vertices[face[i+1]]);
q.vmult(b,b);
q.vmult(c,c);
x.vadd(b,b);
x.vadd(c,c);
var distance = intersectPoint.distanceTo(from);
if(!(pointInTriangle(intersectPoint, a, b, c) || pointInTriangle(intersectPoint, b, a, c)) || distance > fromToDistance){
continue;
}
this.reportIntersection(normal, intersectPoint, shape, body, fi);
}
// }
}
};
Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype.intersectConvex;
var intersectTrimesh_normal = new Vec3();
var intersectTrimesh_localDirection = new Vec3();
var intersectTrimesh_localFrom = new Vec3();
var intersectTrimesh_localTo = new Vec3();
var intersectTrimesh_worldNormal = new Vec3();
var intersectTrimesh_worldIntersectPoint = new Vec3();
var intersectTrimesh_localAABB = new AABB();
var intersectTrimesh_triangles = [];
var intersectTrimesh_treeTransform = new Transform();
/**
* @method intersectTrimesh
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
* @param {object} [options]
* @todo Optimize by transforming the world to local space first.
* @todo Use Octree lookup
*/
Ray.prototype.intersectTrimesh = function intersectTrimesh(
mesh,
quat,
position,
body,
options
){
var normal = intersectTrimesh_normal;
var triangles = intersectTrimesh_triangles;
var treeTransform = intersectTrimesh_treeTransform;
var minDistNormal = intersectConvex_minDistNormal;
var vector = intersectConvex_vector;
var minDistIntersect = intersec