diff --git a/index.html b/index.html
index da6d15622..e5f54abed 100644
--- a/index.html
+++ b/index.html
@@ -88,7 +88,7 @@
-
+
@@ -137,7 +137,7 @@
sceneEl.components.inspector.openInspector();
document.querySelector('.viewer-header-wrapper').style.display = 'none';
}
-
+
AFRAME.registerComponent('timed-inspector', {
init: function() {
setTimeout( function () {
diff --git a/src/aframe-streetmix-parsers.js b/src/aframe-streetmix-parsers.js
index 204ed00a5..877136ade 100644
--- a/src/aframe-streetmix-parsers.js
+++ b/src/aframe-streetmix-parsers.js
@@ -196,6 +196,7 @@ function getZPositions (start, end, step) {
function createSidewalkClonedVariants (segmentWidthInMeters, density, elevationPosY = 0, streetLength, direction = 'random', animated = false) {
const xValueRange = [-(0.37 * segmentWidthInMeters), (0.37 * segmentWidthInMeters)];
const zValueRange = getZPositions((-0.5 * streetLength), (0.5 * streetLength), 1.5);
+
const densityFactors = {
empty: 0,
sparse: 0.03,
@@ -207,7 +208,7 @@ function createSidewalkClonedVariants (segmentWidthInMeters, density, elevationP
dividerParentEl.setAttribute('position', { y: elevationPosY });
// Randomly generate avatars
for (let i = 0; i < totalPedestrianNumber; i++) {
- const variantName = (animated === true) ? 'a_char' + String(getRandomIntInclusive(1, 8)) : 'char' + String(getRandomIntInclusive(1, 16));
+ const variantName = 'a_char' + String(getRandomIntInclusive(1, 8));
const xVal = getRandomArbitrary(xValueRange[0], xValueRange[1]);
const zVal = zValueRange.pop();
const yVal = 0;
@@ -216,14 +217,21 @@ function createSidewalkClonedVariants (segmentWidthInMeters, density, elevationP
let animationDirection = 'inbound';
placedObjectEl.setAttribute('position', { x: xVal, y: yVal, z: zVal });
placedObjectEl.setAttribute('mixin', variantName);
+ placedObjectEl.setAttribute('position', { x: xVal, y: yVal, z: zVal });
// Roughly 50% of traffic will be incoming
if (Math.random() < 0.5 && direction === 'random') {
placedObjectEl.setAttribute('rotation', '0 180 0');
animationDirection = 'outbound';
}
- if (animated) {
- addLinearStreetAnimation(placedObjectEl, 1.4, streetLength, xVal, yVal, zVal, animationDirection);
+ if (animated) {
+ placedObjectEl.setAttribute('automation-element', {
+ zPos: zVal,
+ direction: animationDirection,
+ speed: 1.4,
+ streetLength: streetLength,
+ mixer: true
+ });
}
dividerParentEl.append(placedObjectEl);
}
@@ -313,37 +321,6 @@ function createBusElement (variantList, length, showVehicles) {
return busParentEl;
}
-function addLinearStreetAnimation (reusableObjectEl, speed, streetLength, xPos, yVal = 0, zPos, direction) {
- const totalStreetDuration = (streetLength / speed) * 1000; // time in milliseconds
- const halfStreet = (direction === 'outbound')
- ? -streetLength / 2
- : streetLength / 2;
- const startingDistanceToTravel = Math.abs(halfStreet - zPos);
- const startingDuration = (startingDistanceToTravel / speed) * 1000;
-
- const animationAttrs_1 = {
- property: 'position',
- easing: 'linear',
- loop: 'false',
- from: { x: xPos, y: yVal, z: zPos },
- to: { z: halfStreet },
- dur: startingDuration
- };
- const animationAttrs_2 = {
- property: 'position',
- easing: 'linear',
- loop: 'true',
- from: { x: xPos, y: yVal, z: -halfStreet },
- to: { x: xPos, y: yVal, z: halfStreet },
- delay: startingDuration,
- dur: totalStreetDuration
- };
- reusableObjectEl.setAttribute('animation__1', animationAttrs_1);
- reusableObjectEl.setAttribute('animation__2', animationAttrs_2);
-
- return reusableObjectEl;
-}
-
function createDriveLaneElement (variantList, segmentWidthInMeters, streetLength, animated = false, showVehicles = true, count = 1, carStep = undefined) {
if (!showVehicles) {
return;
@@ -373,11 +350,7 @@ function createDriveLaneElement (variantList, segmentWidthInMeters, streetLength
} else {
rotationY = rotationVariants[lineVariant];
}
- /*
- if (carType === 'pedestrian') {
- return createSidewalkClonedVariants(segmentWidthInMeters, 'normal', streetLength, lineVariant, animated);
- }
-*/
+
const driveLaneParentEl = document.createElement('a-entity');
if (variantList.length == 1) {
@@ -430,7 +403,12 @@ function createDriveLaneElement (variantList, segmentWidthInMeters, streetLength
reusableObjectEl.setAttribute('wheel',
{ speed: speed, wheelDiameter: params['wheelDiameter'] }
);
- addLinearStreetAnimation(reusableObjectEl, speed, streetLength, 0, 0, positionZ, direction);
+ reusableObjectEl.setAttribute('automation-element', {
+ zPos: positionZ,
+ direction: direction,
+ speed: speed,
+ streetLength: streetLength
+ });
}
driveLaneParentEl.append(reusableObjectEl);
return reusableObjectEl;
@@ -554,7 +532,14 @@ function createMicroMobilityElement (variantList, segmentType, posY = 0, length,
if (animated) {
reusableObjectEl.setAttribute('animation-mixer', '');
const speed = 5;
- addLinearStreetAnimation(reusableObjectEl, speed, length, 0, posY, randPosZ, variantList[0]);
+ reusableObjectEl.setAttribute('automation-element', {
+ zPos: randPosZ,
+ direction: variantList[0],
+ speed: speed,
+ streetLength: length,
+ mixer: true
+ });
+
}
if (segmentType === 'bike-lane') {
mixinId = cyclistMixins[getRandomIntInclusive(0, countCyclist)];
diff --git a/src/components/automation.js b/src/components/automation.js
new file mode 100644
index 000000000..c5df3499e
--- /dev/null
+++ b/src/components/automation.js
@@ -0,0 +1,109 @@
+/* global AFRAME */
+/*
+The automation-element component controls all animation of the element
+*/
+AFRAME.registerComponent('automation-element', {
+ schema: {
+ // initial z position of element
+ zPos: { type: 'number', default: 0 },
+ direction: { type: 'string', default: 'outbound', oneOf: ['outbound', 'inbound'] },
+ enabled: { type: 'boolean', default: true },
+ speed: { type: 'number', default: 1000 },
+ streetLength: { type: 'number', default: 60 }
+ },
+ init: function () {
+ const el = this.el;
+ this.addLinearAnimation();
+ // save position after pause animation (switch to Editor mode)
+ let animPausePos = new THREE.Vector3;
+ // flag to skip initial animation play
+ let firstPlayFlag = true;
+
+ el.addEventListener('play', (evt) => {
+ if (!firstPlayFlag && !el.object3D.position.equals(animPausePos)) {
+ // the object's position has been changed in the Editor. Update animation
+ this.addLinearAnimation();
+ }
+ firstPlayFlag = false;
+ });
+ el.addEventListener('pause', () => {
+ // save position while animation pause (switch to the Editor mode)
+ const pos = el.object3D.position;
+ animPausePos.copy(pos);
+ });
+ el.addEventListener('animationcomplete', () => {
+ // move the object to the beginning of the path
+ let pos = el.object3D.position;
+ pos.z = -pos.z;
+ el.setAttribute('position', pos);
+ el.removeAttribute('animation');
+ // change animtaion settings
+ this.addLinearAnimation();
+ });
+ },
+ addLinearAnimation: function () {
+ const el = this.el;
+ const streetLength = this.data.streetLength;
+ const speed = this.data.speed;
+ const direction = this.data.direction;
+ const zPos = el.object3D.position.z;
+ //const zPos = this.data.zPos;
+
+ const totalStreetDuration = (streetLength / speed) * 1000; // time in milliseconds
+
+ if (direction === 'outbound') {
+ halfStreet = -streetLength / 2;
+ el.setAttribute('rotation', {y: 180});
+ } else {
+ halfStreet = streetLength / 2;
+ el.setAttribute('rotation', {y: 0});
+ }
+ const startingDistanceToTravel = Math.abs(halfStreet - zPos);
+ const startingDuration = (startingDistanceToTravel / speed) * 1000;
+
+ // animation params to move an object from its current position to the end of street
+ // in a specified direction
+ const animationAttrs_1 = {
+ property: 'object3D.position.z',
+ easing: 'linear',
+ loop: 'false',
+ from: zPos,
+ to: halfStreet,
+ dur: startingDuration
+ };
+
+ el.setAttribute('animation', animationAttrs_1);
+ },
+ toggleAnimation: function (enabled) {
+ const el = this.el;
+ const elemComponents = el.components;
+ if (elemComponents['animation']) {
+ // toggle animations that bypass play/pause events that are called by the aframe-inspector
+ elemComponents['animation'].initialized = enabled;
+ };
+
+ if (this.data.mixer) el.setAttribute('animation-mixer', {timeScale: 1 * enabled});
+
+ if (elemComponents['wheel']) {
+ elemComponents['wheel'].data.isActive = enabled;
+ }
+ },
+ update: function (oldData) {
+ // If `oldData` is empty, then this means we're in the initialization process.
+ // No need to update.
+ if (Object.keys(oldData).length === 0) { return; }
+
+ const changedData = AFRAME.utils.diff(this.data, oldData);
+ const changedKeysNumber = Object.keys(changedData).length;
+
+ if (changedData.hasOwnProperty('enabled')) {
+ this.toggleAnimation(changedData.enabled);
+ // if only 'enabled' data changed
+ if (changedKeysNumber == 1) return;
+ }
+
+ if (changedKeysNumber > 0) {
+ this.addLinearAnimation();
+ }
+ }
+});
diff --git a/src/index.js b/src/index.js
index e1b51f153..c7262ebc1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -11,6 +11,7 @@ require('./assets.js');
require('./components/notify.js');
require('./components/create-from-json');
require('./components/screentock.js');
+require('./components/automation.js');
require('aframe-atlas-uvs-component');
AFRAME.registerComponent('street', {
@@ -22,11 +23,28 @@ AFRAME.registerComponent('street', {
showGround: { default: true },
showStriping: { default: true },
showVehicles: { default: true },
- globalAnimated: { default: false },
+ globalAnimated: { default: true },
length: { default: 60 } // new default of 60 from 0.4.4
},
+ init: function () {
+ this.asceneElem = document.querySelector('a-scene');
+ },
+ toggleGlobalAnimation: function (globalAnimated) {
+ const animatedElements = document.querySelectorAll('a-entity[automation-element]');
+ animatedElements.forEach(animatedElem => {
+ animatedElem.setAttribute('automation-element', `enabled: ${globalAnimated}`);
+ });
+ },
update: function (oldData) { // fired once at start and at each subsequent change of a schema value
- var data = this.data;
+ const data = this.data;
+
+ const changedData = AFRAME.utils.diff(data, oldData);
+
+ if (Object.keys(changedData).length == 1 && changedData.hasOwnProperty('globalAnimated')) {
+ // if only globalAnimated option is changed
+ this.toggleGlobalAnimation(data.globalAnimated);
+ return;
+ }
if (data.JSON.length === 0) {
if (oldData.JSON !== undefined && oldData.JSON.length === 0) { return; } // this has happened before, surpress console log
@@ -498,7 +516,8 @@ AFRAME.registerComponent('street-environment', {
AFRAME.registerComponent('wheel', {
schema: {
speed: { type: 'number', default: 1 },
- wheelDiameter: { type: 'number', default: 1 }
+ wheelDiameter: { type: 'number', default: 1 },
+ isActive: { type: 'boolean', default: true}
},
init: function () {
@@ -521,6 +540,8 @@ AFRAME.registerComponent('wheel', {
});
},
tick: function (t, dt) {
+ if (!this.data.isActive) return;
+
const speed = this.data.speed / 1000;
const wheelDiameter = this.data.wheelDiameter;