Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ AbstractColliderComponent.prototype.attached = function (entity) {
*/
AbstractColliderComponent.prototype.detached = function (/*entity*/) {
this.entity = null;
this.system = null;
};

/**
Expand Down
18 changes: 17 additions & 1 deletion src/goo/addons/physicspack/components/ColliderComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ ColliderComponent.type = 'ColliderComponent';
* Initialize the collider as a static rigid body in the physics world.
*/
ColliderComponent.prototype.initialize = function () {
var entity = this.entity;

var material = null;
if (this.material) {
material = new CANNON.Material();
Expand All @@ -56,10 +58,11 @@ ColliderComponent.prototype.initialize = function () {
var cannonShape = this.cannonShape = ColliderComponent.getCannonShape(this.worldCollider);
cannonShape.material = material;

this.updateLayerAndMask();

cannonShape.collisionResponse = !this.isTrigger;

// Get transform from entity
var entity = this.entity;
var transform = entity.transformComponent.sync().worldTransform;
var position = new CANNON.Vec3();
var quaternion = new CANNON.Quaternion();
Expand Down Expand Up @@ -168,4 +171,17 @@ ColliderComponent.applyOnEntity = function (obj, entity) {
}
};

ColliderComponent.prototype.updateLayerAndMask = function () {
ColliderComponent.updateLayerAndMask(this.entity);
};

ColliderComponent.updateLayerAndMask = function (entity) {
var layer = entity.layer;
var cannonShape = entity.colliderComponent.cannonShape;
if (cannonShape) {
cannonShape.collisionFilterMask = entity._world.getSystem('PhysicsSystem').getLayerMask(layer);
cannonShape.collisionFilterGroup = Math.pow(2, layer);
}
};

module.exports = ColliderComponent;
2 changes: 2 additions & 0 deletions src/goo/addons/physicspack/components/RigidBodyComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,8 @@ RigidBodyComponent.prototype.addCollider = function (entity, position, quaternio

cannonShape.collisionResponse = !colliderComponent.isTrigger;

ColliderComponent.updateLayerAndMask(entity);

// Add the shape
var cannonPos = new CANNON.Vec3();
if (position) {
Expand Down
53 changes: 52 additions & 1 deletion src/goo/addons/physicspack/systems/PhysicsSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,63 @@ function PhysicsSystem(settings) {

this.initialized = false;

// Collision masks
this.masks = [];
for (var i=0; i<32; i++) {
this.masks.push(-1); // Everything collides with everything by default
}

AbstractPhysicsSystem.call(this, 'PhysicsSystem', ['RigidBodyComponent']);
}

PhysicsSystem.prototype = Object.create(AbstractPhysicsSystem.prototype);
PhysicsSystem.prototype.constructor = PhysicsSystem;

/**
* Make the system ignore (or un-ignore) collisions between layerA or layerB.
* @param {number} layerA
* @param {number} layerB
* @param {boolean} [ignore=true]
*/
PhysicsSystem.prototype.ignoreLayerCollision = function (layerA, layerB, ignore) {
ignore = ignore !== undefined ? ignore : true;
var maskA = Math.pow(2, layerA);
var maskB = Math.pow(2, layerB);
var masks = this.masks;
if (ignore) {
masks[layerA] &= ~maskB;
masks[layerB] &= ~maskA;
} else {
masks[layerA] |= maskB;
masks[layerB] |= maskA;
}

this.updateLayersAndMasks();
};

/**
* @param {number} layerA
* @param {number} layerB
*/
PhysicsSystem.prototype.getIgnoreLayerCollision = function (layerA, layerB) {
return !(this.masks[layerA] & Math.pow(2, layerB));
};

PhysicsSystem.prototype.updateLayersAndMasks = function () {
var entities = this._activeColliderEntities;
for (var i=0; i<entities.length; i++) {
entities[i].colliderComponent.updateLayerAndMask();
}
};

/**
* Returns the current layer mask for the given layer.
* @param {number} layer
* @return {number}
*/
PhysicsSystem.prototype.getLayerMask = function (layer) {
return this.masks[layer];
};

/**
* @param {Vector3} gravityVector
*/
Expand Down
22 changes: 22 additions & 0 deletions src/goo/entities/Entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,33 @@ function Entity(world, name, id) {
*/
this.static = false;

this._layer = 0;

Entity.entityCount++;
}
Entity.prototype = Object.create(EventTarget.prototype);
Entity.prototype.constructor = Entity;

Object.defineProperties(Entity.prototype, {

/**
* The layer the entity is in. A layer is in the range 0 to 31. Layers can be used for selective rendering from cameras or ignoring raycasts.
* @target-class Entity layer member
* @type {number}
*/
layer: {
get: function () {
return this._layer;
},
set: function (value) {
this._layer = value;
this.fire({
type: 'layerChanged'
});
}
}
});

//! AT: not sure if 'add' is a better name - need to search for something short and compatible with the other 'set' methods
/**
* Sets components on the entity or tries to create and set components out of the supplied parameters.
Expand Down
8 changes: 8 additions & 0 deletions src/goo/entities/World.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var Entity = require('./Entity');
var EntityManager = require('./managers/EntityManager');
var TransformComponent = require('./components/TransformComponent');
var Manager = require('./managers/Manager');
var LayerManager = require('./managers/LayerManager');
var System = require('./systems/System');
var Component = require('./components/Component');
var EntitySelection = require('./EntitySelection');
Expand Down Expand Up @@ -76,6 +77,7 @@ function World(options) {
this._managers = [];
this._systems = [];

// Such antipattern. Should just add/change/remove entities directly.
this._addedEntities = [];
this._changedEntities = [];
this._removedEntities = [];
Expand All @@ -88,6 +90,12 @@ function World(options) {
*/
this.entityManager = new EntityManager();
this.setManager(this.entityManager);

/**
* @type {LayerManager}
*/
this.layerManager = new LayerManager();
this.setManager(this.layerManager);

this._components = [];

Expand Down
4 changes: 1 addition & 3 deletions src/goo/entities/managers/EntityManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ var EntitySelection = require('../../entities/EntitySelection');
* @extends Manager
*/
function EntityManager() {
Manager.call(this);

this.type = 'EntityManager';
Manager.call(this, { type: 'EntityManager' });

this._entitiesById = new Map();
this._entitiesByIndex = new Map();
Expand Down
81 changes: 81 additions & 0 deletions src/goo/entities/managers/LayerManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
var Manager = require('./Manager');

/**
* Handles all layers in the world.
* @extends Manager
*/
function LayerManager() {
Manager.call(this, { type: 'LayerManager' });

// All the layer names
this.layers = [];

for (var i=0; i<32; i++) {
var name;
if (i === 0) {
name = 'Default';
} else {
name = 'Layer ' + i;
}
this.layers.push(name);
}
}
LayerManager.prototype = Object.create(Manager.prototype);
LayerManager.prototype.constructor = LayerManager;

/**
* Returns a layer mask given an array of layers.
* @param {array} layers
* @return {number}
*/
LayerManager.prototype.getMaskFromLayers = function (layers) {
var mask = 0;
var l = layers.length;
while (l--) {
mask |= Math.pow(2, layers[l]);
}
return mask;
};

/**
* Set the name of a layer.
* @param {number} layer
* @param {string} name
*/
LayerManager.prototype.setName = function (layer, name) {
this.layers[layer] = name;
};

/**
* Returns a layer mask given a set of layer names.
* @param {array} names
* @returns {number}
*/
LayerManager.prototype.getMaskFromNames = function (names) {
var l = names.length;
var layers = new Array(l);
while (l--) {
layers[l] = this.nameToLayer(names[l]);
}
return this.getMaskFromLayers(layers);
};

/**
* Returns the name of a layer.
* @param {number} layer
* @returns {string}
*/
LayerManager.prototype.layerToName = function (layer) {
return this.layers[layer];
};

/**
* Returns the layer for the given name, or -1 if the name does not exist.
* @param {string} name
* @returns {number}
*/
LayerManager.prototype.nameToLayer = function (name) {
return this.layers.indexOf(name);
};

module.exports = LayerManager;
19 changes: 18 additions & 1 deletion src/goo/entities/managers/Manager.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
/**
* Base class for managers.
* @param {object} [options]
* @param {string} [options.type]
*/
function Manager() {
function Manager(options) {
options = options || {};

this.type = options.type;
this.installedAPI = {};
}

/**
* Called when an entity was added to the World.
* @param {Entity} entity
*/
Manager.prototype.added = function (/*entity*/) {};

/**
* Called when an entity was removed from the World.
* @param {Entity} entity
*/
Manager.prototype.removed = function (/*entity*/) {};

Manager.prototype.applyAPI = function (worldBy) {
var api = this.api;
for (var key in api) {
Expand Down
1 change: 1 addition & 0 deletions src/goo/loaders/handlers/EntityHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ EntityHandler.prototype._update = function (ref, config, options) {
if (!entity) { return; }
entity.id = ref;
entity.name = config.name;
entity.layer = config.layer !== undefined ? config.layer : 0;
entity.static = !!config.static;

updateTags(entity, config.tags);
Expand Down
15 changes: 15 additions & 0 deletions test/unit/addons/physicspack/components/ColliderComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ describe('ColliderComponent', function () {
expect(colliderComponent.worldCollider.radius).toBe(3);
});

it('updates its mask and layer', function () {
var colliderComponent = new ColliderComponent({
collider: new SphereCollider({ radius: 1 })
});
var entity = world.createEntity(colliderComponent).addToWorld();
var layer = 4;
entity.layer = layer;
system.ignoreLayerCollision(4,5);

colliderComponent.initialize();

expect(colliderComponent.cannonShape.collisionFilterGroup).toBe(Math.pow(2, 4));
expect(colliderComponent.cannonShape.collisionFilterMask).toBe(-1 & (~Math.pow(2, 5)));
});

it('instantiates as a static body without a rigid body component', function () {
var material = new PhysicsMaterial({
friction: 0.6,
Expand Down
22 changes: 20 additions & 2 deletions test/unit/addons/physicspack/systems/PhysicsSystem-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ describe('PhysicsSystem', function () {
rbc.initialize(); // Needed to initialize body

var result = new RaycastResult();
system.raycastAny(start, direction, distance, { collisionGroup: -1 }, result);
system.raycastAny(start, direction, distance, { collisionMask: -1 }, result);
expect(result.entity).toBeTruthy();

result = new RaycastResult();
system.raycastAny(start, direction, distance, { collisionGroup: 2 }, result);
system.raycastAny(start, direction, distance, { collisionMask: 2 }, result);
expect(result.entity).toBeFalsy();
});

Expand Down Expand Up @@ -793,4 +793,22 @@ describe('PhysicsSystem', function () {
system.play();
world.fixedUpdate();
});

it('can ignore layer collision', function () {
system.ignoreLayerCollision(4,5);

expect(system.getIgnoreLayerCollision(4,5)).toBeTruthy();
expect(system.getIgnoreLayerCollision(4,6)).toBeFalsy();

system.ignoreLayerCollision(4,5, false);

expect(system.getIgnoreLayerCollision(4,5)).toBeFalsy();
});

it('can get a layer mask', function () {
system.ignoreLayerCollision(4,5);

var mask = system.getLayerMask(4);
expect(mask).toBe(-1 & (~Math.pow(2, 5))); // Everything minus 5
});
});