Merge pull request #187 from supermedium/recenter

recenter
This commit is contained in:
Kevin Ngo
2018-11-17 02:00:35 -08:00
committed by GitHub
6 changed files with 329 additions and 470 deletions

View File

@@ -1,76 +1,48 @@
/** /**
* Pivot the scene when user enters VR to face the links. * Recenter camera back to facing down negative Z axis at 0, 0, 0.
*/ */
AFRAME.registerComponent('recenter', { AFRAME.registerComponent('recenter', {
schema: { schema: {
enabled: {default: true}, enabled: {default: true}
target: {default: ''}
}, },
init: function () { init: function () {
var sceneEl = this.el.sceneEl; var sceneEl = this.el.sceneEl;
this.matrix = new THREE.Matrix4();
this.frustum = new THREE.Frustum();
this.rotationOffset = 0;
this.euler = new THREE.Euler();
this.euler.order = 'YXZ';
this.menuPosition = new THREE.Vector3();
this.recenter = this.recenter.bind(this); this.recenter = this.recenter.bind(this);
this.checkInViewAfterRecenter = this.checkInViewAfterRecenter.bind(this);
this.target = document.querySelector(this.data.target);
// Delay to make sure we have a valid pose. document.querySelectorAll('[tracked-controls]').forEach(controlEl => {
sceneEl.addEventListener('enter-vr', () => setTimeout(this.recenter, 100)); controlEl.addEventListener('menudown', this.recenter);
// User can also recenter the menu manually. controlEl.addEventListener('thumbstickdown', this.recenter);
sceneEl.addEventListener('menudown', this.recenter); });
sceneEl.addEventListener('thumbstickdown', this.recenter);
window.addEventListener('vrdisplaypresentchange', this.recenter);
}, },
recenter: function () { recenter: (function () {
var euler = this.euler; var euler = new THREE.Euler();
if (!this.data.enabled) { return; } var matrix = new THREE.Matrix4();
euler.setFromRotationMatrix(this.el.sceneEl.camera.el.object3D.matrixWorld, 'YXZ'); var rotationMatrix = new THREE.Matrix4();
this.el.object3D.rotation.y = euler.y + this.rotationOffset; var translationMatrix = new THREE.Matrix4();
// Check if the menu is in camera frustum in next tick after a frame has rendered.
setTimeout(this.checkInViewAfterRecenter, 0);
},
/*
* Sometimes the quaternion returns the yaw in the [-180, 180] range.
* Check if the menu is in the camera frustum after recenter it to
* decide if we apply an offset or not.
*/
checkInViewAfterRecenter: (function () {
var bottomVec3 = new THREE.Vector3();
var topVec3 = new THREE.Vector3();
return function () { return function () {
var camera = this.el.sceneEl.camera; const el = this.el;
var frustum = this.frustum;
var menuPosition = this.menuPosition;
camera.updateMatrix(); if (!this.data.enabled) { return; }
camera.updateMatrixWorld();
frustum.setFromMatrix(this.matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
// Check if menu position (and its bounds) are within the frustum. const camera = el.sceneEl.camera.el.object3D;
// Check bounds in case looking angled up or down, rather than menu central.
menuPosition.setFromMatrixPosition(this.target.object3D.matrixWorld);
bottomVec3.copy(menuPosition).y -= 3;
topVec3.copy(menuPosition).y += 3;
if (frustum.containsPoint(menuPosition) || // Reset matrix.
frustum.containsPoint(bottomVec3) || matrix.identity();
frustum.containsPoint(topVec3)) { return; }
this.rotationOffset = this.rotationOffset === 0 ? Math.PI : 0; // Create matrix to reset Y rotation.
// Recenter again with the new offset. euler.set(0, -1 * camera.rotation.y, 0);
this.recenter(); rotationMatrix.makeRotationFromEuler(euler);
// Create matrix to zero position.
translationMatrix.makeTranslation(-1 * camera.position.x, 0, -1 * camera.position.z);
// Multiply and decompose back to object3D.
matrix.multiply(rotationMatrix).multiply(translationMatrix);
matrix.decompose(el.object3D.position, el.object3D.quaternion, el.object3D.scale);
el.object3D.updateMatrixWorld(true);
}; };
})(), })()
remove: function () {
this.el.sceneEl.removeEventListener('enter-vr', this.recenter);
}
}); });

View File

@@ -33,7 +33,7 @@
effect-bloom="strength: 1" effect-bloom="strength: 1"
gpu-preloader gpu-preloader
loading-screen="backgroundColor: #000;" loading-screen="backgroundColor: #000;"
overlay="objects: #rightHand, #leftHand, [mixin~='cursorMesh'], .overlay" overlay="objects: .overlay"
pool__beat-arrow-blue="mixin: arrowBlueBeat; size: 10; container: #beatContainer" pool__beat-arrow-blue="mixin: arrowBlueBeat; size: 10; container: #beatContainer"
pool__beat-arrow-red="mixin: arrowRedBeat; size: 10; container: #beatContainer" pool__beat-arrow-red="mixin: arrowRedBeat; size: 10; container: #beatContainer"
pool__beat-dot-blue="mixin: dotBlueBeat; size: 10; container: #beatContainer" pool__beat-dot-blue="mixin: dotBlueBeat; size: 10; container: #beatContainer"
@@ -92,13 +92,18 @@
id="sparkParticles" id="sparkParticles"
particleplayer="src: #sparksJSON; color: #fcc; pscale: 0.7; on: explode; scale: 0.3; loop: false; img: #spark2Img; dur: 700; count: 50%; animateScale: true; initialScale: 3 0.5 1; finalScale: 0.1 0.5 1"></a-entity> particleplayer="src: #sparksJSON; color: #fcc; pscale: 0.7; on: explode; scale: 0.3; loop: false; img: #spark2Img; dur: 700; count: 50%; animateScale: true; initialScale: 3 0.5 1; finalScale: 0.1 0.5 1"></a-entity>
<!-- Player. -->
<a-mixin <a-mixin
id="cursorMesh" id="cursorMesh"
material="shader: flat; transparent: true; src: #cursorMeshImg; depthTest: false" material="shader: flat; transparent: true; src: #cursorMeshImg; depthTest: false"
sub-object="from: #cursorLaser; name: glow"></a-mixin> sub-object="from: #cursorLaser; name: glow"></a-mixin>
<a-entity id="cameraRig">
<!--
Player.
Apply overlay to camera rig, not controllers, because overlay will reparent to
an empty scene, disabling ability to move and rotate the camera rig having effect
on controller matrix world.
-->
<a-entity id="cameraRig" class="overlay" bind__recenter="enabled: !isPlaying">
<a-entity id="camera" position="0 1.6 0.5" camera look-controls wasd-controls> <a-entity id="camera" position="0 1.6 0.5" camera look-controls wasd-controls>
<a-entity <a-entity
id="cameraCollider" id="cameraCollider"
@@ -180,6 +185,7 @@
<a-entity <a-entity
id="{{ hand }}CursorMesh" id="{{ hand }}CursorMesh"
class="overlay"
mixin="cursorMesh" mixin="cursorMesh"
bind__cursor-mesh="active: {{ hand }}RaycasterActive && !isPLaying" bind__cursor-mesh="active: {{ hand }}RaycasterActive && !isPLaying"
cursor-mesh="cursorEl: #{{ hand }}Hand" cursor-mesh="cursorEl: #{{ hand }}Hand"
@@ -205,8 +211,12 @@
mixin="raycaster" mixin="raycaster"
cursor="rayOrigin: mouse" cursor="rayOrigin: mouse"
raycaster="objects: [raycastable]"></a-entity> raycaster="objects: [raycastable]"></a-entity>
<a-entity id="mouseCursorMesh" mixin="cursorMesh" cursor-mesh="cursorEl: #mouseCursor" <a-entity
bind__cursor-mesh="active: menuActive"></a-entity> id="mouseCursorMesh"
class="overlay"
mixin="cursorMesh"
cursor-mesh="cursorEl: #mouseCursor"
bind__cursor-mesh="active: menuActive"></a-entity>
{% endif %} {% endif %}
</a-scene> </a-scene>

View File

@@ -66294,7 +66294,7 @@ var dummyGeometry = new THREE.Geometry();
module.exports.Component = registerComponent('geometry', { module.exports.Component = registerComponent('geometry', {
schema: { schema: {
buffer: {default: true}, buffer: {default: true},
primitive: {default: 'box', oneOf: geometryNames}, primitive: {default: 'box', oneOf: geometryNames, schemaChange: true},
skipCache: {default: false} skipCache: {default: false}
}, },
@@ -66956,7 +66956,11 @@ module.exports.Component = registerComponent('light', {
distance: {default: 0.0, min: 0, if: {type: ['point', 'spot']}}, distance: {default: 0.0, min: 0, if: {type: ['point', 'spot']}},
intensity: {default: 1.0, min: 0, if: {type: ['ambient', 'directional', 'hemisphere', 'point', 'spot']}}, intensity: {default: 1.0, min: 0, if: {type: ['ambient', 'directional', 'hemisphere', 'point', 'spot']}},
penumbra: {default: 0, min: 0, max: 1, if: {type: ['spot']}}, penumbra: {default: 0, min: 0, max: 1, if: {type: ['spot']}},
type: {default: 'directional', oneOf: ['ambient', 'directional', 'hemisphere', 'point', 'spot']}, type: {
default: 'directional',
oneOf: ['ambient', 'directional', 'hemisphere', 'point', 'spot'],
schemaChange: true
},
target: {type: 'selector', if: {type: ['spot', 'directional']}}, target: {type: 'selector', if: {type: ['spot', 'directional']}},
// Shadows. // Shadows.
@@ -68130,7 +68134,7 @@ module.exports.Component = registerComponent('material', {
offset: {type: 'vec2', default: {x: 0, y: 0}}, offset: {type: 'vec2', default: {x: 0, y: 0}},
opacity: {default: 1.0, min: 0.0, max: 1.0}, opacity: {default: 1.0, min: 0.0, max: 1.0},
repeat: {type: 'vec2', default: {x: 1, y: 1}}, repeat: {type: 'vec2', default: {x: 1, y: 1}},
shader: {default: 'standard', oneOf: shaderNames}, shader: {default: 'standard', oneOf: shaderNames, schemaChange: true},
side: {default: 'front', oneOf: ['front', 'back', 'double']}, side: {default: 'front', oneOf: ['front', 'back', 'double']},
transparent: {default: false}, transparent: {default: false},
vertexColors: {type: 'string', default: 'none', oneOf: ['face', 'vertex']}, vertexColors: {type: 'string', default: 'none', oneOf: ['face', 'vertex']},
@@ -69852,11 +69856,11 @@ module.exports.Component = registerComponent('pool', {
var el; var el;
if (this.availableEls.length === 0) { if (this.availableEls.length === 0) {
if (this.data.dynamic === false) { if (this.data.dynamic === false) {
warn('Requested entity from empty pool: ' + this.name); warn('Requested entity from empty pool: ' + this.attrName);
return; return;
} else { } else {
warn('Requested entity from empty pool. This pool is dynamic and will resize ' + warn('Requested entity from empty pool. This pool is dynamic and will resize ' +
'automatically. You might want to increase its initial size: ' + this.name); 'automatically. You might want to increase its initial size: ' + this.attrName);
} }
this.createEntity(); this.createEntity();
} }
@@ -69872,7 +69876,7 @@ module.exports.Component = registerComponent('pool', {
returnEntity: function (el) { returnEntity: function (el) {
var index = this.usedEls.indexOf(el); var index = this.usedEls.indexOf(el);
if (index === -1) { if (index === -1) {
warn('The returned entity was not previously pooled from ' + this.name); warn('The returned entity was not previously pooled from ' + this.attrName);
return; return;
} }
this.usedEls.splice(index, 1); this.usedEls.splice(index, 1);
@@ -70572,6 +70576,8 @@ module.exports.Component = registerComponent('sound', {
multiple: true, multiple: true,
init: function () { init: function () {
var self = this;
this.listener = null; this.listener = null;
this.audioLoader = new THREE.AudioLoader(); this.audioLoader = new THREE.AudioLoader();
this.pool = new THREE.Group(); this.pool = new THREE.Group();
@@ -70579,7 +70585,7 @@ module.exports.Component = registerComponent('sound', {
this.mustPlay = false; this.mustPlay = false;
// Don't pass evt because playSound takes a function as parameter. // Don't pass evt because playSound takes a function as parameter.
this.playSoundBound = () => { this.playSound(); }; this.playSoundBound = function () { self.playSound(); };
}, },
update: function (oldData) { update: function (oldData) {
@@ -71467,7 +71473,6 @@ module.exports.Component = registerComponent('tracked-controls', {
// Apply transforms, if 6DOF and in VR. // Apply transforms, if 6DOF and in VR.
if (vrDisplay && pose.position) { if (vrDisplay && pose.position) {
standingMatrix = this.el.sceneEl.camera.el.components['look-controls'].controls.getStandingMatrix(); standingMatrix = this.el.sceneEl.camera.el.components['look-controls'].controls.getStandingMatrix();
object3D.matrixAutoUpdate = false;
object3D.matrix.compose(object3D.position, object3D.quaternion, object3D.scale); object3D.matrix.compose(object3D.position, object3D.quaternion, object3D.scale);
object3D.matrix.multiplyMatrices(standingMatrix, object3D.matrix); object3D.matrix.multiplyMatrices(standingMatrix, object3D.matrix);
object3D.matrix.decompose(object3D.position, object3D.quaternion, object3D.scale); object3D.matrix.decompose(object3D.position, object3D.quaternion, object3D.scale);
@@ -71476,9 +71481,6 @@ module.exports.Component = registerComponent('tracked-controls', {
object3D.rotateX(this.data.orientationOffset.x * THREE.Math.DEG2RAD); object3D.rotateX(this.data.orientationOffset.x * THREE.Math.DEG2RAD);
object3D.rotateY(this.data.orientationOffset.y * THREE.Math.DEG2RAD); object3D.rotateY(this.data.orientationOffset.y * THREE.Math.DEG2RAD);
object3D.rotateZ(this.data.orientationOffset.z * THREE.Math.DEG2RAD); object3D.rotateZ(this.data.orientationOffset.z * THREE.Math.DEG2RAD);
object3D.updateMatrix();
object3D.matrixWorldNeedsUpdate = true;
}, },
/** /**
@@ -72864,6 +72866,7 @@ var warn = utils.debug('core:a-entity:warn');
var MULTIPLE_COMPONENT_DELIMITER = '__'; var MULTIPLE_COMPONENT_DELIMITER = '__';
var OBJECT3D_COMPONENTS = ['position', 'rotation', 'scale', 'visible']; var OBJECT3D_COMPONENTS = ['position', 'rotation', 'scale', 'visible'];
var ONCE = {once: true};
/** /**
* Entity is a container object that components are plugged into to comprise everything in * Entity is a container object that components are plugged into to comprise everything in
@@ -73320,6 +73323,7 @@ var proto = Object.create(ANode.prototype, {
updateComponent: { updateComponent: {
value: function (attr, attrValue, clobber) { value: function (attr, attrValue, clobber) {
var component = this.components[attr]; var component = this.components[attr];
if (component) { if (component) {
// Remove component. // Remove component.
if (attrValue === null && !checkComponentDefined(this, attr)) { if (attrValue === null && !checkComponentDefined(this, attr)) {
@@ -73440,12 +73444,62 @@ var proto = Object.create(ANode.prototype, {
} }
}, },
/**
* When mixins updated, trigger init or optimized-update of relevant components.
*/
mixinUpdate: { mixinUpdate: {
value: function (newMixins, oldMixins) { value: (function () {
oldMixins = oldMixins || this.getAttribute('mixin'); var componentsUpdated = [];
this.updateMixins(newMixins, oldMixins);
this.updateComponents(); return function (newMixins, oldMixins) {
} var component;
var mixinEl;
var mixinIds;
var i;
var self = this;
if (!this.hasLoaded) {
this.addEventListener('loaded', function () {
self.mixinUpdate(newMixins, oldMixins);
}, ONCE);
return;
}
oldMixins = oldMixins || this.getAttribute('mixin');
mixinIds = this.updateMixins(newMixins, oldMixins);
// Loop over current mixins.
componentsUpdated.length = 0;
for (i = 0; i < this.mixinEls.length; i++) {
for (component in this.mixinEls[i].componentCache) {
if (componentsUpdated.indexOf(component) === -1) {
if (this.components[component]) {
// Update. Just rebuild data.
this.components[component].handleMixinUpdate();
} else {
// Init. buildData will gather mixin values.
this.initComponent(component, null);
}
componentsUpdated.push(component);
}
}
}
// Loop over old mixins to call for data rebuild.
for (i = 0; i < mixinIds.oldMixinIds.length; i++) {
mixinEl = document.getElementById(mixinIds.oldMixinIds[i]);
if (!mixinEl) { continue; }
for (component in mixinEl.componentCache) {
if (componentsUpdated.indexOf(component) === -1) {
if (this.components[component]) {
// Component removed. Rebuild data if not yet rebuilt.
this.components[component].handleMixinUpdate();
}
}
}
}
};
})()
}, },
/** /**
@@ -73958,6 +74012,7 @@ module.exports = registerElement('a-node', {
value: (function () { value: (function () {
var newMixinIdArray = []; var newMixinIdArray = [];
var oldMixinIdArray = []; var oldMixinIdArray = [];
var mixinIds = {};
return function (newMixins, oldMixins) { return function (newMixins, oldMixins) {
var i; var i;
@@ -73969,6 +74024,9 @@ module.exports = registerElement('a-node', {
newMixinIds = newMixins ? utils.split(newMixins.trim(), /\s+/) : newMixinIdArray; newMixinIds = newMixins ? utils.split(newMixins.trim(), /\s+/) : newMixinIdArray;
oldMixinIds = oldMixins ? utils.split(oldMixins.trim(), /\s+/) : oldMixinIdArray; oldMixinIds = oldMixins ? utils.split(oldMixins.trim(), /\s+/) : oldMixinIdArray;
mixinIds.newMixinIds = newMixinIds;
mixinIds.oldMixinIds = oldMixinIds;
// Unregister old mixins. // Unregister old mixins.
for (i = 0; i < oldMixinIds.length; i++) { for (i = 0; i < oldMixinIds.length; i++) {
if (newMixinIds.indexOf(oldMixinIds[i]) === -1) { if (newMixinIds.indexOf(oldMixinIds[i]) === -1) {
@@ -73990,6 +74048,8 @@ module.exports = registerElement('a-node', {
window.HTMLElement.prototype.setAttribute.call(this, 'mixin', window.HTMLElement.prototype.setAttribute.call(this, 'mixin',
this.computedMixinStr); this.computedMixinStr);
} }
return mixinIds;
}; };
})() })()
}, },
@@ -74315,8 +74375,6 @@ var Component = module.exports.Component = function (el, attrValue, id) {
this.oldData = this.isObjectBased ? this.objectPool.use() : undefined; this.oldData = this.isObjectBased ? this.objectPool.use() : undefined;
this.previousOldData = this.isObjectBased ? this.objectPool.use() : undefined; this.previousOldData = this.isObjectBased ? this.objectPool.use() : undefined;
this.parsingAttrValue = this.isObjectBased ? this.objectPool.use() : undefined; this.parsingAttrValue = this.isObjectBased ? this.objectPool.use() : undefined;
// Purely for deciding to skip type checking.
this.previousAttrValue = undefined;
// Last value passed to updateProperties. // Last value passed to updateProperties.
this.throttledEmitComponentChanged = utils.throttle(function emitChange () { this.throttledEmitComponentChanged = utils.throttle(function emitChange () {
@@ -74509,7 +74567,7 @@ Component.prototype = {
}, },
/** /**
* Apply new component data if data has changed. * Apply new component data if data has changed (from setAttribute).
* *
* @param {string} attrValue - HTML attribute value. * @param {string} attrValue - HTML attribute value.
* If undefined, use the cached attribute value and continue updating properties. * If undefined, use the cached attribute value and continue updating properties.
@@ -74517,10 +74575,6 @@ Component.prototype = {
*/ */
updateProperties: function (attrValue, clobber) { updateProperties: function (attrValue, clobber) {
var el = this.el; var el = this.el;
var key;
var initialOldData;
var skipTypeChecking;
var hasComponentChanged;
// Just cache the attribute if the entity has not loaded // Just cache the attribute if the entity has not loaded
// Components are not initialized until the entity has loaded // Components are not initialized until the entity has loaded
@@ -74529,91 +74583,144 @@ Component.prototype = {
return; return;
} }
// Disable type checking if the passed attribute is an object and has not changed.
skipTypeChecking = attrValue !== null && typeof this.previousAttrValue === 'object' &&
attrValue === this.previousAttrValue;
if (skipTypeChecking) {
for (key in this.attrValue) {
if (!(key in attrValue)) {
skipTypeChecking = false;
break;
}
}
for (key in attrValue) {
if (!(key in this.attrValue)) {
skipTypeChecking = false;
break;
}
}
}
// Cache previously passed attribute to decide if we skip type checking.
this.previousAttrValue = attrValue;
// Parse the attribute value. // Parse the attribute value.
// Cache current attrValue for future updates. Updates `this.attrValue`. // Cache current attrValue for future updates. Updates `this.attrValue`.
attrValue = this.parseAttrValueForCache(attrValue); // `null` means no value on purpose, do not set a default value, let mixins take over.
if (attrValue !== null) {
// Update previous attribute value to later decide if we skip type checking. attrValue = this.parseAttrValueForCache(attrValue);
this.previousAttrValue = attrValue; }
// Cache current attrValue for future updates. // Cache current attrValue for future updates.
this.updateCachedAttrValue(attrValue, clobber); this.updateCachedAttrValue(attrValue, clobber);
if (this.updateSchema) { this.updateSchema(this.buildData(this.attrValue, false, true)); } if (this.initialized) {
this.data = this.buildData(this.attrValue, clobber, false, skipTypeChecking); this.updateComponent(attrValue, clobber);
this.callUpdateHandler();
if (!this.initialized) {
// Component is being already initialized.
if (el.initializingComponents[this.name]) { return; }
// Prevent infinite loop in case of init method setting same component on the entity.
el.initializingComponents[this.name] = true;
// Initialize component.
this.init();
this.initialized = true;
delete el.initializingComponents[this.name];
// Store current data as previous data for future updates.
this.oldData = extendProperties(this.oldData, this.data, this.isObjectBased);
// For oldData, pass empty object to multiple-prop schemas or object single-prop schema.
// Pass undefined to rest of types.
initialOldData = this.isObjectBased ? this.objectPool.use() : undefined;
this.update(initialOldData);
if (this.isObjectBased) { this.objectPool.recycle(initialOldData); }
// Play the component if the entity is playing.
if (el.isPlaying) { this.play(); }
el.emit('componentinitialized', this.evtDetail, false);
} else { } else {
// Store the previous old data before we calculate the new oldData. this.initComponent();
if (this.previousOldData instanceof Object) {
utils.objectPool.clearObject(this.previousOldData);
}
if (this.isObjectBased) {
copyData(this.previousOldData, this.oldData);
} else {
this.previousOldData = this.oldData;
}
hasComponentChanged = !utils.deepEqual(this.oldData, this.data);
// Don't update if properties haven't changed.
// Always update rotation, position, scale.
if (!this.isPositionRotationScale && !hasComponentChanged) { return; }
// Store current data as previous data for future updates.
// Reuse `this.oldData` object to try not to allocate another one.
if (this.oldData instanceof Object) { utils.objectPool.clearObject(this.oldData); }
this.oldData = extendProperties(this.oldData, this.data, this.isObjectBased);
// Update component with the previous old data.
this.update(this.previousOldData);
this.throttledEmitComponentChanged();
} }
}, },
initComponent: function () {
var el = this.el;
var initialOldData;
// Build data.
if (this.updateSchema) { this.updateSchema(this.buildData(this.attrValue, false, true)); }
this.data = this.buildData(this.attrValue);
// Component is being already initialized.
if (el.initializingComponents[this.name]) { return; }
// Prevent infinite loop in case of init method setting same component on the entity.
el.initializingComponents[this.name] = true;
// Initialize component.
this.init();
this.initialized = true;
delete el.initializingComponents[this.name];
// Store current data as previous data for future updates.
this.oldData = extendProperties(this.oldData, this.data, this.isObjectBased);
// For oldData, pass empty object to multiple-prop schemas or object single-prop schema.
// Pass undefined to rest of types.
initialOldData = this.isObjectBased ? this.objectPool.use() : undefined;
this.update(initialOldData);
if (this.isObjectBased) { this.objectPool.recycle(initialOldData); }
// Play the component if the entity is playing.
if (el.isPlaying) { this.play(); }
el.emit('componentinitialized', this.evtDetail, false);
},
/**
* @param attrValue - Passed argument from setAttribute.
*/
updateComponent: function (attrValue, clobber) {
var key;
var mayNeedSchemaUpdate;
if (clobber) {
// Clobber. Rebuild.
if (this.updateSchema) {
this.updateSchema(this.buildData(this.attrValue, true, true));
}
this.data = this.buildData(this.attrValue, true, false);
return;
}
// Apply new value to this.data in place since direct update.
if (this.isSingleProperty) {
// Single-property (already parsed).
this.data = attrValue;
return;
}
parseProperties(attrValue, this.schema, true, this.name);
// Check if we need to update schema.
if (this.schemaChangeKeys.length) {
for (key in attrValue) {
if (this.schema[key].schemaChange) {
mayNeedSchemaUpdate = true;
break;
}
}
}
if (mayNeedSchemaUpdate) {
// Rebuild data if need schema update.
if (this.updateSchema) {
this.updateSchema(this.buildData(this.attrValue, true, true));
}
this.data = this.buildData(this.attrValue, true, false);
return;
}
// Normal update.
for (key in attrValue) {
if (attrValue[key] === undefined) { continue; }
this.data[key] = attrValue[key];
}
},
/**
* Check if component should fire update and fire update lifecycle handler.
*/
callUpdateHandler: function () {
var hasComponentChanged;
// Store the previous old data before we calculate the new oldData.
if (this.previousOldData instanceof Object) {
utils.objectPool.clearObject(this.previousOldData);
}
if (this.isObjectBased) {
copyData(this.previousOldData, this.oldData);
} else {
this.previousOldData = this.oldData;
}
hasComponentChanged = !utils.deepEqual(this.oldData, this.data);
// Don't update if properties haven't changed.
// Always update rotation, position, scale.
if (!this.isPositionRotationScale && !hasComponentChanged) { return; }
// Store current data as previous data for future updates.
// Reuse `this.oldData` object to try not to allocate another one.
if (this.oldData instanceof Object) { utils.objectPool.clearObject(this.oldData); }
this.oldData = extendProperties(this.oldData, this.data, this.isObjectBased);
// Update component with the previous old data.
this.update(this.previousOldData);
this.throttledEmitComponentChanged();
},
handleMixinUpdate: function () {
this.data = this.buildData(this.attrValue);
this.callUpdateHandler();
},
/** /**
* Reset value of a property to the property's default value. * Reset value of a property to the property's default value.
* If single-prop component, reset value to component's default value. * If single-prop component, reset value to component's default value.
@@ -74621,11 +74728,13 @@ Component.prototype = {
* @param {string} propertyName - Name of property to reset. * @param {string} propertyName - Name of property to reset.
*/ */
resetProperty: function (propertyName) { resetProperty: function (propertyName) {
if (!this.isObjectBased) { if (this.isObjectBased) {
this.attrValue = undefined;
} else {
if (!(propertyName in this.attrValue)) { return; } if (!(propertyName in this.attrValue)) { return; }
delete this.attrValue[propertyName]; delete this.attrValue[propertyName];
this.data[propertyName] = this.schema[propertyName].default;
} else {
this.attrValue = this.schema.default;
this.data = this.schema.default;
} }
this.updateProperties(this.attrValue); this.updateProperties(this.attrValue);
}, },
@@ -74650,10 +74759,7 @@ Component.prototype = {
}, },
/** /**
* Build component data from the current state of the entity, ultimately * Build component data from the current state of the entity.data.
* updating this.data.
*
* If the component was detached completely, set data to null.
* *
* Precedence: * Precedence:
* 1. Defaults data * 1. Defaults data
@@ -74665,10 +74771,9 @@ Component.prototype = {
* @param {object} newData - Element new data. * @param {object} newData - Element new data.
* @param {boolean} clobber - The previous data is completely replaced by the new one. * @param {boolean} clobber - The previous data is completely replaced by the new one.
* @param {boolean} silent - Suppress warning messages. * @param {boolean} silent - Suppress warning messages.
* @param {boolean} skipTypeChecking - Skip type checking and coercion.
* @return {object} The component data * @return {object} The component data
*/ */
buildData: function (newData, clobber, silent, skipTypeChecking) { buildData: function (newData, clobber, silent) {
var componentDefined; var componentDefined;
var data; var data;
var defaultValue; var defaultValue;
@@ -74687,7 +74792,7 @@ Component.prototype = {
if (this.isObjectBased) { utils.objectPool.clearObject(nextData); } if (this.isObjectBased) { utils.objectPool.clearObject(nextData); }
// 1. Default values (lowest precendence). // 1. Gather default values (lowest precendence).
if (this.isSingleProperty) { if (this.isSingleProperty) {
if (this.isObjectBased) { if (this.isObjectBased) {
// If object-based single-prop, then copy over the data to our pooled object. // If object-based single-prop, then copy over the data to our pooled object.
@@ -74718,17 +74823,16 @@ Component.prototype = {
} }
} }
// 2. Mixin values. // 2. Gather mixin values.
for (i = 0; i < mixinEls.length; i++) { for (i = 0; i < mixinEls.length; i++) {
mixinData = mixinEls[i].getAttribute(this.attrName); mixinData = mixinEls[i].getAttribute(this.attrName);
if (!mixinData) { continue; } if (!mixinData) { continue; }
data = extendProperties(data, mixinData, this.isObjectBased); data = extendProperties(data, mixinData, this.isObjectBased);
} }
// 3. Attribute values (highest precendence). // 3. Gather attribute values (highest precendence).
if (componentDefined) { if (componentDefined) {
if (this.isSingleProperty) { if (this.isSingleProperty) {
if (skipTypeChecking === true) { return newData; }
// If object-based, copy the value to not modify the original. // If object-based, copy the value to not modify the original.
if (isObject(newData)) { if (isObject(newData)) {
copyData(this.parsingAttrValue, newData); copyData(this.parsingAttrValue, newData);
@@ -74738,12 +74842,10 @@ Component.prototype = {
} }
data = extendProperties(data, newData, this.isObjectBased); data = extendProperties(data, newData, this.isObjectBased);
} else { } else {
if (skipTypeChecking === true) { return data; }
// Parse and coerce using the schema. // Parse and coerce using the schema.
if (this.isSingleProperty) { return parseProperty(data, schema); } if (this.isSingleProperty) { return parseProperty(data, schema); }
} }
if (skipTypeChecking === true) { return data; }
return parseProperties(data, schema, undefined, this.name, silent); return parseProperties(data, schema, undefined, this.name, silent);
} }
}; };
@@ -74762,7 +74864,10 @@ if (window.debug) {
*/ */
module.exports.registerComponent = function (name, definition) { module.exports.registerComponent = function (name, definition) {
var NewComponent; var NewComponent;
var propertyName;
var proto = {}; var proto = {};
var schema;
var schemaIsSingleProp;
// Warning if component is statically registered after the scene. // Warning if component is statically registered after the scene.
if (document.currentScript && document.currentScript !== aframeScript) { if (document.currentScript && document.currentScript !== aframeScript) {
@@ -74808,32 +74913,47 @@ module.exports.registerComponent = function (name, definition) {
'Check that you are not loading two versions of the same component ' + 'Check that you are not loading two versions of the same component ' +
'or two different components of the same name.'); 'or two different components of the same name.');
} }
NewComponent = function (el, attr, id) { NewComponent = function (el, attr, id) {
Component.call(this, el, attr, id); Component.call(this, el, attr, id);
}; };
NewComponent.prototype = Object.create(Component.prototype, proto); NewComponent.prototype = Object.create(Component.prototype, proto);
NewComponent.prototype.name = name; NewComponent.prototype.name = name;
NewComponent.prototype.isPositionRotationScale = name === 'position' || name === 'rotation' || name === 'scale'; NewComponent.prototype.isPositionRotationScale =
name === 'position' || name === 'rotation' || name === 'scale';
NewComponent.prototype.constructor = NewComponent; NewComponent.prototype.constructor = NewComponent;
NewComponent.prototype.system = systems && systems.systems[name]; NewComponent.prototype.system = systems && systems.systems[name];
NewComponent.prototype.play = wrapPlay(NewComponent.prototype.play); NewComponent.prototype.play = wrapPlay(NewComponent.prototype.play);
NewComponent.prototype.pause = wrapPause(NewComponent.prototype.pause); NewComponent.prototype.pause = wrapPause(NewComponent.prototype.pause);
NewComponent.prototype.remove = wrapRemove(NewComponent.prototype.remove); NewComponent.prototype.remove = wrapRemove(NewComponent.prototype.remove);
schema = utils.extend(processSchema(NewComponent.prototype.schema,
NewComponent.prototype.name));
schemaIsSingleProp = isSingleProp(NewComponent.prototype.schema);
// Keep track of keys that may potentially change the schema.
if (!schemaIsSingleProp) {
NewComponent.prototype.schemaChangeKeys = [];
for (propertyName in schema) {
if (schema[propertyName].schemaChange) {
NewComponent.prototype.schemaChangeKeys.push(propertyName);
}
}
}
// Create object pool for class of components. // Create object pool for class of components.
objectPools[name] = utils.objectPool.createPool(); objectPools[name] = utils.objectPool.createPool();
components[name] = { components[name] = {
Component: NewComponent, Component: NewComponent,
dependencies: NewComponent.prototype.dependencies, dependencies: NewComponent.prototype.dependencies,
isSingleProp: isSingleProp(NewComponent.prototype.schema), isSingleProp: schemaIsSingleProp,
multiple: NewComponent.prototype.multiple, multiple: NewComponent.prototype.multiple,
name: name, name: name,
parse: NewComponent.prototype.parse, parse: NewComponent.prototype.parse,
parseAttrValueForCache: NewComponent.prototype.parseAttrValueForCache, parseAttrValueForCache: NewComponent.prototype.parseAttrValueForCache,
schema: utils.extend(processSchema(NewComponent.prototype.schema, schema: schema,
NewComponent.prototype.name)),
stringify: NewComponent.prototype.stringify, stringify: NewComponent.prototype.stringify,
type: NewComponent.prototype.type type: NewComponent.prototype.type
}; };
@@ -75115,6 +75235,7 @@ var warn = debug('core:propertyTypes:warn');
var propertyTypes = module.exports.propertyTypes = {}; var propertyTypes = module.exports.propertyTypes = {};
var nonCharRegex = /[,> .[\]:]/; var nonCharRegex = /[,> .[\]:]/;
var urlRegex = /\url\((.+)\)/;
// Built-in property types. // Built-in property types.
registerPropertyType('audio', '', assetParse); registerPropertyType('audio', '', assetParse);
@@ -75186,7 +75307,7 @@ function assetParse (value) {
if (typeof value !== 'string') { return value; } if (typeof value !== 'string') { return value; }
// Wrapped `url()` in case of data URI. // Wrapped `url()` in case of data URI.
parsedUrl = value.match(/\url\((.+)\)/); parsedUrl = value.match(urlRegex);
if (parsedUrl) { return parsedUrl[1]; } if (parsedUrl) { return parsedUrl[1]; }
// ID. // ID.
@@ -76476,7 +76597,10 @@ module.exports.parseProperties = (function () {
var propValue; var propValue;
propNames.length = 0; propNames.length = 0;
for (propName in (getPartialData ? propData : schema)) { propNames.push(propName); } for (propName in (getPartialData ? propData : schema)) {
if (getPartialData && propData[propName] === undefined) { continue; }
propNames.push(propName);
}
if (propData === null || typeof propData !== 'object') { return propData; } if (propData === null || typeof propData !== 'object') { return propData; }
@@ -77857,17 +77981,6 @@ registerGeometry('triangle', {
}); });
},{"../core/geometry":109,"../lib/three":157}],155:[function(_dereq_,module,exports){ },{"../core/geometry":109,"../lib/three":157}],155:[function(_dereq_,module,exports){
// Check before the polyfill runs.
window.hasNativeWebVRImplementation = !!window.navigator.getVRDisplays || !!window.navigator.getVRDevices;
// WebVR polyfill
var WebVRPolyfill = _dereq_('webvr-polyfill');
window.webvrpolyfill = new WebVRPolyfill({
BUFFER_SCALE: 1,
CARDBOARD_UI_DISABLED: true,
ROTATE_INSTRUCTIONS_DISABLED: true
});
var utils = _dereq_('./utils/'); var utils = _dereq_('./utils/');
var debug = utils.debug; var debug = utils.debug;
@@ -77893,11 +78006,22 @@ if (window.location.protocol === 'file:') {
// Polyfill `Promise`. // Polyfill `Promise`.
window.Promise = window.Promise || _dereq_('promise-polyfill'); window.Promise = window.Promise || _dereq_('promise-polyfill');
// WebVR polyfill
// Check before the polyfill runs.
window.hasNativeWebVRImplementation = !!window.navigator.getVRDisplays ||
!!window.navigator.getVRDevices;
var WebVRPolyfill = _dereq_('webvr-polyfill');
var polyfillConfig = {
BUFFER_SCALE: 1,
CARDBOARD_UI_DISABLED: true,
ROTATE_INSTRUCTIONS_DISABLED: true
};
// Workaround for iOS Safari canvas sizing issues in stereo (webvr-polyfill/issues/102). // Workaround for iOS Safari canvas sizing issues in stereo (webvr-polyfill/issues/102).
// Only for iOS on versions older than 10. // Only for iOS on versions older than 10.
if (utils.device.isIOSOlderThan10(window.navigator.userAgent)) { if (utils.device.isIOSOlderThan10(window.navigator.userAgent)) {
window.WebVRConfig.BUFFER_SCALE = 1 / window.devicePixelRatio; polyfillConfig.BUFFER_SCALE = 1 / window.devicePixelRatio;
} }
window.webvrpolyfill = new WebVRPolyfill(polyfillConfig);
_dereq_('present'); // Polyfill `performance.now()`. _dereq_('present'); // Polyfill `performance.now()`.
@@ -77937,7 +78061,7 @@ _dereq_('./core/a-mixin');
_dereq_('./extras/components/'); _dereq_('./extras/components/');
_dereq_('./extras/primitives/'); _dereq_('./extras/primitives/');
console.log('A-Frame Version: 0.8.2 (Date 2018-11-07, Commit #ffa3bdf4)'); console.log('A-Frame Version: 0.8.2 (Date 2018-11-16, Commit #f9484114)');
console.log('three Version:', pkg.dependencies['three']); console.log('three Version:', pkg.dependencies['three']);
console.log('WebVR Polyfill Version:', pkg.dependencies['webvr-polyfill']); console.log('WebVR Polyfill Version:', pkg.dependencies['webvr-polyfill']);
@@ -79900,7 +80024,7 @@ module.exports.isLandscape = function () {
* Check if device is iOS and older than version 10. * Check if device is iOS and older than version 10.
*/ */
module.exports.isIOSOlderThan10 = function (userAgent) { module.exports.isIOSOlderThan10 = function (userAgent) {
return /(iphone|ipod|ipad).*os.(7|8|9)/i.test(userAgent); return /(iphone|ipod|ipad).*os.(7_|8_|9_)/i.test(userAgent);
}; };
/** /**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long