Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animation tasks (from "Animation prevent editing" issue) #435

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
<!-- uncomment the line below to load all possible asset categories -->
<!-- <street-assets categories="sidewalk-props people people-rigged vehicles vehicles-rigged buildings intersection-props segment-textures segment-colors lane-separator stencils vehicles-transit dividers sky grounds"></street-assets> -->
<!-- a reduced set of assets for non-animated streetmix streets without intersections -->
<street-assets categories="loud-bicycle sidewalk-props people vehicles vehicles-rigged buildings segment-textures segment-colors lane-separator stencils vehicles-transit dividers sky grounds"></street-assets>
<street-assets categories="loud-bicycle sidewalk-props people-rigged vehicles vehicles-rigged buildings segment-textures segment-colors lane-separator stencils vehicles-transit dividers sky grounds"></street-assets>
</a-assets>

<a-entity id="street-container" data-layer-name="3D Street Layers" data-layer-show-children>
Expand Down Expand Up @@ -137,7 +137,7 @@
sceneEl.components.inspector.openInspector();
document.querySelector('.viewer-header-wrapper').style.display = 'none';
}

AFRAME.registerComponent('timed-inspector', {
init: function() {
setTimeout( function () {
Expand Down
67 changes: 26 additions & 41 deletions src/aframe-streetmix-parsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)];
Expand Down
109 changes: 109 additions & 0 deletions src/components/automation.js
Original file line number Diff line number Diff line change
@@ -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();
}
}
});
27 changes: 24 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand All @@ -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
Expand Down Expand Up @@ -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 () {
Expand All @@ -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;

Expand Down
Loading