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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/showcase/tracked-controls/components/grab.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* Updates its position to move along the controller.
*/
AFRAME.registerComponent('grab', {
after: ['tracked-controls-webxr'],
before: ['aabb-collider'],
init: function () {
this.GRABBED_STATE = 'grabbed';
// Bind event handlers
Expand Down Expand Up @@ -67,6 +69,7 @@ AFRAME.registerComponent('grab', {
y: position.y + this.deltaPosition.y,
z: position.z + this.deltaPosition.z
});
hitEl.object3D.updateMatrixWorld();
},

updateDelta: function () {
Expand Down
2 changes: 2 additions & 0 deletions src/components/cursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ module.exports.Component = registerComponent('cursor', {
rayOrigin: {default: 'entity', oneOf: ['mouse', 'entity', 'xrselect']}
},

after: ['tracked-controls'],

multiple: true,

init: function () {
Expand Down
2 changes: 2 additions & 0 deletions src/components/generic-tracked-controller-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ module.exports.Component = registerComponent('generic-tracked-controller-control
disabled: {default: false}
},

after: ['tracked-controls'],

/**
* Button IDs:
* 0 - trackpad
Expand Down
2 changes: 2 additions & 0 deletions src/components/hand-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ module.exports.Component = registerComponent('hand-controls', {
handModelStyle: {default: 'lowPoly', oneOf: ['lowPoly', 'highPoly', 'toon']}
},

after: ['tracked-controls'],

init: function () {
var self = this;
var el = this.el;
Expand Down
2 changes: 2 additions & 0 deletions src/components/hand-tracking-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ module.exports.Component = registerComponent('hand-tracking-controls', {
modelOpacity: {default: 1.0}
},

after: ['tracked-controls'],

bindMethods: function () {
this.onControllersUpdate = this.onControllersUpdate.bind(this);
this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this);
Expand Down
2 changes: 2 additions & 0 deletions src/components/oculus-touch-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ module.exports.Component = registerComponent('oculus-touch-controls', {
orientationOffset: {type: 'vec3', default: {x: 43, y: 0, z: 0}}
},

after: ['tracked-controls'],

mapping: INPUT_MAPPING,

bindMethods: function () {
Expand Down
4 changes: 4 additions & 0 deletions src/components/tracked-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ module.exports.Component = registerComponent('tracked-controls', {
space: {type: 'string', oneOf: ['targetRaySpace', 'gripSpace'], default: 'targetRaySpace'}
},

// Run after both tracked-controls-webvr and tracked-controls-webxr to allow other components
// to be after either without having to list them both.
after: ['tracked-controls-webvr', 'tracked-controls-webxr'],

update: function () {
var data = this.data;
var el = this.el;
Expand Down
2 changes: 2 additions & 0 deletions src/components/valve-index-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ module.exports.Component = registerComponent('valve-index-controls', {
orientationOffset: {type: 'vec3'}
},

after: ['tracked-controls'],

mapping: {
axes: {
trackpad: [0, 1],
Expand Down
2 changes: 2 additions & 0 deletions src/components/vive-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ module.exports.Component = registerComponent('vive-controls', {
orientationOffset: {type: 'vec3'}
},

after: ['tracked-controls'],

mapping: INPUT_MAPPING,

init: function () {
Expand Down
2 changes: 2 additions & 0 deletions src/components/vive-focus-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ module.exports.Component = registerComponent('vive-focus-controls', {
armModel: {default: true}
},

after: ['tracked-controls'],

mapping: INPUT_MAPPING,

bindMethods: function () {
Expand Down
1 change: 1 addition & 0 deletions src/components/wasd-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports.Component = registerComponent('wasd-controls', {
wsEnabled: {default: true},
wsInverted: {default: false}
},
after: ['look-controls'],

init: function () {
// To keep track of the pressed keys.
Expand Down
2 changes: 2 additions & 0 deletions src/components/windows-motion-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ module.exports.Component = registerComponent('windows-motion-controls', {
hideDisconnected: {default: true}
},

after: ['tracked-controls'],

mapping: INPUT_MAPPING,

bindMethods: function () {
Expand Down
8 changes: 8 additions & 0 deletions src/core/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,8 @@ module.exports.registerComponent = function (name, definition) {
components[name] = {
Component: NewComponent,
dependencies: NewComponent.prototype.dependencies,
before: NewComponent.prototype.before,
after: NewComponent.prototype.after,
isSingleProperty: NewComponent.prototype.isSingleProperty,
isObjectBased: NewComponent.prototype.isObjectBased,
multiple: NewComponent.prototype.multiple,
Expand All @@ -702,6 +704,12 @@ module.exports.registerComponent = function (name, definition) {
schema: schema,
stringify: NewComponent.prototype.stringify
};

// Notify all scenes
for (var i = 0; i < scenes.length; i++) {
scenes[i].emit('componentregistered', {name: name}, false);
}

return NewComponent;
};

Expand Down
167 changes: 148 additions & 19 deletions src/core/scene/a-scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ var initWakelock = require('./wakelock');
var loadingScreen = require('./loadingScreen');
var scenes = require('./scenes');
var systems = require('../system').systems;
var components = require('../component').components;
var THREE = require('../../lib/three');
var utils = require('../../utils/');
var warn = utils.debug('core:a-scene:warn');
// Require after.
var AEntity = require('../a-entity').AEntity;
var ANode = require('../a-node').ANode;
Expand Down Expand Up @@ -56,7 +58,8 @@ class AScene extends AEntity {
self.time = self.delta = 0;
self.usedOfferSession = false;

self.behaviors = {tick: [], tock: []};
self.componentOrder = [];
self.behaviors = {};
self.hasLoaded = false;
self.isPlaying = false;
self.originalHTML = self.innerHTML;
Expand Down Expand Up @@ -129,6 +132,12 @@ class AScene extends AEntity {
});

this.initSystems();
// Compute component order
this.componentOrder = determineComponentBehaviorOrder(components, this.componentOrder);
this.addEventListener('componentregistered', function () {
// Recompute order
self.componentOrder = determineComponentBehaviorOrder(components, self.componentOrder);
});

// WebXR Immersive navigation handler.
if (this.hasWebXR && navigator.xr && navigator.xr.addEventListener) {
Expand Down Expand Up @@ -210,16 +219,32 @@ class AScene extends AEntity {
* @param {object} behavior - A component.
*/
addBehavior (behavior) {
var behaviorArr;
var behaviors = this.behaviors;
var behaviorSet;
var behaviors = this.behaviors[behavior.name];
var behaviorType;

if (!behaviors) {
behaviors = this.behaviors[behavior.name] = {
tick: { inUse: false, array: [], markedForRemoval: [] },
tock: { inUse: false, array: [], markedForRemoval: [] }
};
}

// Check if behavior has tick and/or tock and add the behavior to the appropriate list.
for (behaviorType in behaviors) {
if (!behavior[behaviorType]) { continue; }
behaviorArr = this.behaviors[behaviorType];
if (behaviorArr.indexOf(behavior) === -1) {
behaviorArr.push(behavior);
behaviorSet = behaviors[behaviorType];

// In case the behaviorSet is in use, make sure this behavior isn't on the removal list.
if (behaviorSet.inUse) {
var index = behaviorSet.markedForRemoval.indexOf(behavior);
if (index !== -1) {
behaviorSet.markedForRemoval.splice(index, 1);
}
}
// Add behavior to the set
if (behaviorSet.array.indexOf(behavior) === -1) {
behaviorSet.array.push(behavior);
}
}
}
Expand Down Expand Up @@ -522,18 +547,30 @@ class AScene extends AEntity {
* @param {object} behavior - A component.
*/
removeBehavior (behavior) {
var behaviorArr;
var behaviorSet;
var behaviorType;
var behaviors = this.behaviors;
var behaviors = this.behaviors[behavior.name];
var index;

// Check if behavior has tick and/or tock and remove the behavior from the appropriate
// array.
for (behaviorType in behaviors) {
if (!behavior[behaviorType]) { continue; }
behaviorArr = this.behaviors[behaviorType];
index = behaviorArr.indexOf(behavior);
if (index !== -1) { behaviorArr.splice(index, 1); }
behaviorSet = behaviors[behaviorType];
index = behaviorSet.array.indexOf(behavior);
if (index !== -1) {
// Check if the behavior can safely be removed.
if (behaviorSet.inUse) {
// Set is in use, so only mark for removal.
if (behaviorSet.markedForRemoval.indexOf(behavior) === -1) {
behaviorSet.markedForRemoval.push(behavior);
}
} else {
// Swap and remove from the end
behaviorSet.array[index] = behaviorSet.array[behaviorSet.array.length - 1];
behaviorSet.array.pop();
}
}
}
}

Expand Down Expand Up @@ -689,10 +726,7 @@ class AScene extends AEntity {
var systems = this.systems;

// Components.
for (i = 0; i < this.behaviors.tick.length; i++) {
if (!this.behaviors.tick[i].el.isPlaying) { continue; }
this.behaviors.tick[i].tick(time, timeDelta);
}
this.callComponentBehaviors('tick', time, timeDelta);

// Systems.
for (i = 0; i < this.systemNames.length; i++) {
Expand All @@ -711,10 +745,7 @@ class AScene extends AEntity {
var systems = this.systems;

// Components.
for (i = 0; i < this.behaviors.tock.length; i++) {
if (!this.behaviors.tock[i].el.isPlaying) { continue; }
this.behaviors.tock[i].tock(time, timeDelta, camera);
}
this.callComponentBehaviors('tock', time, timeDelta);

// Systems.
for (i = 0; i < this.systemNames.length; i++) {
Expand Down Expand Up @@ -750,8 +781,106 @@ class AScene extends AEntity {
this.object3D.background = savedBackground;
}
}

callComponentBehaviors (behavior, time, timeDelta) {
var i;

for (var c = 0; c < this.componentOrder.length; c++) {
var behaviors = this.behaviors[this.componentOrder[c]];
if (!behaviors) { continue; }
var behaviorSet = behaviors[behavior];

behaviorSet.inUse = true;
for (i = 0; i < behaviorSet.array.length; i++) {
if (!behaviorSet.array[i].el.isPlaying) { continue; }
behaviorSet.array[i][behavior](time, timeDelta);
}
behaviorSet.inUse = false;

// Clean up any behaviors marked for removal
for (i = 0; i < behaviorSet.markedForRemoval.length; i++) {
this.removeBehavior(behaviorSet.markedForRemoval[i]);
}
behaviorSet.markedForRemoval.length = 0;
}
}
}

/**
* Derives an ordering from the components, taking any before and after
* constraints into account.
*
* @param {object} components - The components to order
* @param {array} array - Optional array to use as output
*/
function determineComponentBehaviorOrder (components, array) {
var graph = {};
var i;
var key;
var result = array || [];
result.length = 0;

// Construct graph nodes for each element
for (key in components) {
var element = components[key];
if (element === undefined) { continue; }
var before = element.before ? element.before.slice(0) : [];
var after = element.after ? element.after.slice(0) : [];
graph[key] = { before: before, after: after, visited: false, done: false };
}

// Normalize to after constraints, warn about missing nodes
for (key in graph) {
for (i = 0; i < graph[key].before.length; i++) {
var beforeName = graph[key].before[i];
if (!(beforeName in graph)) {
warn('Invalid ordering constraint, no component named `' + beforeName + '` referenced by `' + key + '`');
continue;
}

graph[beforeName].after.push(key);
}
}

// Perform topological depth-first search
// https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
function visit (name) {
if (!(name in graph) || graph[name].done) {
return;
}

if (graph[name].visited) {
warn('Cycle detected, ignoring one or more before/after constraints. ' +
'The resulting order might be incorrect');
return;
}

graph[name].visited = true;

for (var i = 0; i < graph[name].after.length; i++) {
var afterName = graph[name].after[i];
if (!(afterName in graph)) {
warn('Invalid before/after constraint, no component named `' +
afterName + '` referenced in `' + name + '`');
}
visit(afterName);
}

graph[name].done = true;
result.push(name);
}

for (key in graph) {
if (graph[key].done) {
continue;
}
visit(key);
}
return result;
}

module.exports.determineComponentBehaviorOrder = determineComponentBehaviorOrder;

/**
* Return size constrained to maxSize - maintaining aspect ratio.
*
Expand Down
Loading