Skip to content

Commit

Permalink
Initial distance/proximity culling system
Browse files Browse the repository at this point in the history
  • Loading branch information
xeolabs committed Aug 30, 2013
1 parent 60437dc commit b49812e
Show file tree
Hide file tree
Showing 17 changed files with 1,918 additions and 42 deletions.
142 changes: 142 additions & 0 deletions api/latest/plugins/lib/proximity/proximityEngine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
function ProximityEngine(cfg) {

this._center = [0, 0, 0];
this._radii = [200, 700, 1200];

this._bodies = {};
this._bodyList = [];
this._numBodies = 0;

this._bodyListDirty = false;

this._updatedBodies = [];

if (cfg) {
this.setConfigs(cfg);
}
}

/**
* Configure this proximity engine
* @param cfg
*/
ProximityEngine.prototype.setConfigs = function (cfg) {
if (cfg.center) {
var center = cfg.center;
this._center[0] = center[0];
this._center[1] = center[1];
this._center[2] = center[2];
}
if (cfg.radii != undefined) {
this._radii = cfg.radii;
// Body statuses now redundant because radii now changed
for (var i = 0; i < this._numBodies; i++) {
this._bodyList[i].status = null;
}
}
};

/**
* Add a body
* @param bodyId Unique body ID
* @param cfg Body properties
*/
ProximityEngine.prototype.addBody = function (bodyId, cfg) {
if (this._bodies[bodyId]) {
throw "Body with this ID already added: " + bodyId;
}
this._bodies[bodyId] = new ProximityBody(bodyId, cfg.pos, cfg.radius);
this._bodyListDirty = true;
};

/**
* Remove a body that was added with {@link #addBody}.
* @param bodyId
*/
ProximityEngine.prototype.removeBody = function (bodyId) {
delete this._bodies[bodyId];
this._bodyListDirty = true;
};

/**
* Find updates in proximity status for bodies,
* return an array of the updated bodies in a callback
* @param {Function(Array,Number)} callback Returns array of {@link ProximityBody}s that have changed status,
* plus length of the array
*/
ProximityEngine.prototype.integrate = function (callback) {
if (this._bodyListDirty) {
this._rebuildBodyList();
}
var numUpdatedBodies = 0;
var body;
var dist;
var status;
var outerRadiusIdx = this._radii.length - 1;
for (var i = 0; i < this._numBodies; i++) {
body = this._bodyList[i];
dist = Math.abs(this._lenVec3(this._subVec3(this._center, body.pos, []))); // TODO: optimise - inline this
status = -1;
for (var j = outerRadiusIdx; j >= 0; j--) {
if (dist > this._radii[j]) {
if (j < outerRadiusIdx) {
status = j + 1;
}
break;
}
status = j;
}
if (status !== body.status) {
body.status = status;
this._updatedBodies[numUpdatedBodies++] = body;
}
}
callback(this._updatedBodies, numUpdatedBodies);
};

ProximityEngine.prototype._rebuildBodyList = function () {
this._numBodies = 0;
for (var bodyId in this._bodies) {
if (this._bodies.hasOwnProperty(bodyId)) {
this._bodyList[this._numBodies++] = this._bodies[bodyId];
}
}
this._bodyListDirty = false;
};

ProximityEngine.prototype._subVec3 = function (u, v, dest) {
if (!dest) {
dest = u;
}
dest[0] = u[0] - v[0];
dest[1] = u[1] - v[1];
dest[2] = u[2] - v[2];
return dest;
};

ProximityEngine.prototype._lenVec3 = function (v) {
return Math.sqrt(this._sqLenVec3(v));
};

ProximityEngine.prototype._sqLenVec3 = function (v) {
return this._dotVector3(v, v);
};

ProximityEngine.prototype._dotVector3 = function (u, v) {
return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]);
};


/**
* A spherical body within a {@link ProximityEngine}
* @param bodyId Body ID
* @param pos Center of the body
* @param radius radius of body
* @constructor
*/
function ProximityBody(bodyId, pos, radius) {
this.bodyId = bodyId;
this.pos = pos;
this.radius = radius;
this.status = null;
}
133 changes: 133 additions & 0 deletions api/latest/plugins/lib/proximity/proximityEngineWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* Web worker containing a ProximityEngine
*
* This worker accepts various commands to configure the engine, add or
* remove bodies, and integrate, which means find proximity status changes for bodies.
*
* After each integration, this worker posts back an array buffer containing
* an updated status for each body that has changed status.
*
* Input Commands
* --------------------------------------------------------------------------
*
* Configure the engine:
* {
* cmd: "setConfigs",
* center: [Number, Number, Number,
* innerRadius: Number,
* outerRadius: Number
* }
*
* Create a body:
* {
* cmd: "createBody",
* bodyId: Number,
* bodyCfg: {
* pos: [Number, Number, Number],
* radius: Number
* }
* }
*
* Remove a body:
* {
* cmd: "removeBody",
* bodyId: Number
* }
*
* Update a body:
* {
* cmd: "updateBody",
* bodyId: Number,
* bodyCfg: {
* pos: [Number, Number, Number],
* radius: Number
* }
* }
*
* Integrate the engine:
* {
* cmd: "integrate"
* }
*
* Output Buffer
* --------------------------------------------------------------------------
*
* The output buffer contains a 2-element portion for each proximity body, each of
* which contains the body ID and its proximity status:
*
* [
* bodyId, status,
* bodyId, status,
* ...
* ]
*
*/
importScripts("proximityEngine.js");

// Array in which this worker posts back
// an updated position and direction for each body
var output;

// Proximity engine engine
var engine = new ProximityEngine();

// Set initial default configuration for proximity engine
engine.setConfigs({
center:[0, 0, 0],
radii:[200, 700, 1200]
});

// Handle command from worker owner
addEventListener("message",
function (e) {

var data = e.data;

switch (data.cmd) {

// Configure the proximity engine
case "setConfigs":
engine.setConfigs(data.configs);
break;

// Create a proximity body
case "createBody":
engine.addBody(data.bodyId, data.bodyCfg);
break;

// Update a proximity body
case "updateBody":
engine.updateBody(data.bodyId, data.bodyCfg);
break;

// Remove a proximity body
case "removeBody":
engine.removeBody(data.bodyId);
break;

// Integrate the proximity engine and post back the body updates
case "integrate":
var output = new Int16Array(data.buffer);
var body;
var ibuf = 0;
engine.integrate(
function (updatedBodies, numUpdated) {
for (var i = 0; i < numUpdated; i++) {
body = updatedBodies[i];
output[ibuf++] = body.bodyId;
output[ibuf++] = body.status;
}
// Post the output
var response = {
buffer:output.buffer,
lenOutput:ibuf
};
self.postMessage(response, [response.buffer]);
});
break;

default:
break;
}
}, false);

Loading

0 comments on commit b49812e

Please sign in to comment.