cannon
Version:
A lightweight 3D physics engine written in JavaScript.
351 lines (278 loc) • 11.1 kB
JavaScript
var Vec3 = require("../src/math/Vec3");
var Mat3 = require("../src/math/Mat3");
var Quaternion = require("../src/math/Quaternion");
var Box = require('../src/shapes/Box');
var Body = require('../src/objects/Body');
var Sphere = require('../src/shapes/Sphere');
var World = require('../src/world/World');
var NaiveBroadphase = require('../src/collision/NaiveBroadphase');
var ArrayCollisionMatrix = require('../src/collision/ArrayCollisionMatrix');
var ObjectCollisionMatrix = require('../src/collision/ObjectCollisionMatrix');
var RaycastResult = require('../src/collision/RaycastResult');
module.exports = {
clearForces: function(test){
var world = new World();
var body = new Body();
world.addBody(body);
body.force.set(1,2,3);
body.torque.set(4,5,6);
world.clearForces();
test.ok(body.force.almostEquals(new Vec3(0,0,0)));
test.ok(body.torque.almostEquals(new Vec3(0,0,0)));
test.done();
},
rayTestBox: function(test){
var world = new World();
var body = new Body();
body.addShape(new Box(new Vec3(1, 1, 1)));
world.addBody(body);
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var result = new RaycastResult();
world.rayTest(from, to, result);
test.equal(result.hasHit, true);
test.done();
},
rayTestSphere: function(test){
var world = new World();
var body = new Body();
body.addShape(new Sphere(1));
world.addBody(body);
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var result = new RaycastResult();
world.rayTest(from, to, result);
test.equal(result.hasHit, true);
test.done();
},
raycastClosest: {
single: function(test){
var world = new World();
var body = new Body({
shape: new Sphere(1)
});
world.addBody(body);
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var result = new RaycastResult();
world.raycastClosest(from, to, {}, result);
test.equal(result.hasHit, true);
test.equal(result.body, body);
test.equal(result.shape, body.shapes[0]);
test.done();
},
order: function(test){
var world = new World();
var bodyA = new Body({ shape: new Sphere(1), position: new Vec3(-1,0,0) });
var bodyB = new Body({ shape: new Sphere(1), position: new Vec3(1,0,0) });
world.addBody(bodyA);
world.addBody(bodyB);
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var result = new RaycastResult();
world.raycastClosest(from, to, {}, result);
test.equal(result.hasHit, true);
test.equal(result.body, bodyA);
test.equal(result.shape, bodyA.shapes[0]);
from.set(10, 0, 0);
to.set(-10, 0, 0);
result = new RaycastResult();
world.raycastClosest(from, to, {}, result);
test.equal(result.hasHit, true);
test.equal(result.body, bodyB);
test.equal(result.shape, bodyB.shapes[0]);
test.done();
}
},
raycastAll: {
simple: function(test){
var world = new World();
var body = new Body({ shape: new Sphere(1) });
world.addBody(body);
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var hasHit;
var numResults=0;
var resultBody;
var resultShape;
var returnVal = world.raycastAll(from, to, {}, function (result){
hasHit = result.hasHit;
resultShape = result.shape;
resultBody = result.body;
numResults++;
});
test.equal(returnVal, true, 'should return true on hit');
test.equal(hasHit, true);
test.equal(resultBody, body);
test.equal(numResults, 2);
test.equal(resultShape, resultBody.shapes[0]);
test.done();
},
twoSpheres: function(test){
var world = new World();
var body = new Body({ shape: new Sphere(1) });
world.addBody(body);
var body2 = new Body({ shape: new Sphere(1) });
world.addBody(body2);
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var hasHit = false;
var numResults = 0;
var resultBody;
var resultShape;
world.raycastAll(from, to, {}, function (result){
hasHit = result.hasHit;
resultShape = result.shape;
resultBody = result.body;
numResults++;
});
test.equal(hasHit, true);
test.equal(numResults, 4);
test.done();
},
skipBackFaces: function(test){
var world = new World();
var body = new Body({ shape: new Sphere(1) });
world.addBody(body);
var hasHit = false;
var numResults = 0;
var resultBody;
var resultShape;
world.raycastAll(new Vec3(-10, 0, 0), new Vec3(10, 0, 0), { skipBackfaces: true }, function (result){
hasHit = result.hasHit;
resultShape = result.shape;
resultBody = result.body;
numResults++;
});
test.equal(hasHit, true);
test.equal(numResults, 1);
test.done();
},
collisionFilters: function(test){
var world = new World();
var body = new Body({
shape: new Sphere(1)
});
world.addBody(body);
body.collisionFilterGroup = 2;
body.collisionFilterMask = 2;
var numResults = 0;
world.raycastAll(new Vec3(-10, 0, 0), new Vec3(10, 0, 0), {
collisionFilterGroup: 2,
collisionFilterMask: 2
}, function (result){
numResults++;
});
test.equal(numResults, 2);
numResults = 0;
world.raycastAll(new Vec3(-10, 0, 0), new Vec3(10, 0, 0), {
collisionFilterGroup: 1,
collisionFilterMask: 1
}, function (result){
numResults++;
});
test.equal(numResults, 0, 'should use collision groups!');
test.done();
}
},
raycastAny: function(test){
var world = new World();
world.addBody(new Body({ shape: new Sphere(1) }));
var from = new Vec3(-10, 0, 0);
var to = new Vec3(10, 0, 0);
var result = new RaycastResult();
world.raycastAny(from, to, {}, result);
test.ok(result.hasHit);
test.done();
},
collisionMatrix : function(test) {
function testCollisionMatrix(CollisionMatrix) {
var test_configs = [
{
positions: [
[0,0,0],
[2,0,0],
[0,4,0],
[2,4,0],
[0,8,0],
[2,8,0]
],
colliding: {
'0-1':true,
'2-3':true,
'4-5':true
}
},
{
positions: [
[0,0,0],
[0,4,0],
[0,8,0],
[2,0,0],
[2,4,0],
[2,8,0]
],
colliding: {
'0-3':true,
'1-4':true,
'2-5':true
}
},
{
positions: [
[ 0, 0, 0],
[ 0, 1, 0],
[ 0,10, 0],
[ 0,20, 0],
[ 0,30, 0],
[ 0,40, 0],
[ 0,50, 0],
[ 0,51, 0]
],
colliding: {
'0-1':true,
'6-7':true
}
}
];
for (var config_idx = 0 ; config_idx < test_configs.length; config_idx++) {
var test_config = test_configs[config_idx];
var world = new World();
world.broadphase = new NaiveBroadphase();
world.collisionMatrix = new CollisionMatrix();
world.collisionMatrixPrevious = new CollisionMatrix();
for (var position_idx = 0; position_idx < test_config.positions.length; position_idx++) {
var body = new Body({ mass: 1 });
body.addShape(new Sphere(1.1));
body.position.set.apply(body.position, test_config.positions[position_idx]);
world.add(body);
}
for (var step_idx = 0; step_idx < 2; step_idx++) {
world.step(0.1);
var is_first_step = (step_idx === 0);
for (var coll_i = 0; coll_i < world.bodies.length; coll_i++) {
for (var coll_j = coll_i + 1; coll_j < world.bodies.length; coll_j++) {
var is_colliding_pair = test_config.colliding[coll_i+'-'+coll_j] === true;
var expected = is_colliding_pair;
var is_colliding = is_first_step ?
!!world.collisionMatrix.get(world.bodies[coll_i], world.bodies[coll_j]) :
!!world.collisionMatrixPrevious.get(world.bodies[coll_i], world.bodies[coll_j]);
test.ok(is_colliding === expected,
(expected ? "Should be colliding" : "Should not be colliding") +
': cfg=' + config_idx +
' is_first_step=' + is_first_step +
' is_colliding_pair=' + is_colliding_pair +
' expected=' + expected +
' is_colliding=' + is_colliding +
' i=' + coll_i +
' j=' + coll_j);
}
}
}
}
}
testCollisionMatrix(ArrayCollisionMatrix);
testCollisionMatrix(ObjectCollisionMatrix);
test.done();
},
};