main: work in progress [might break]
This commit is contained in:
parent
58d452447a
commit
84361d303a
|
@ -148,7 +148,7 @@ AFRAME.AComponent.prototype.initComponent = function(initComponent){
|
||||||
|
|
||||||
|
|
||||||
AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
return function(){
|
let setupApp = function(){
|
||||||
updateProperties.apply(this,arguments)
|
updateProperties.apply(this,arguments)
|
||||||
|
|
||||||
if( !this.data || !this.data.uri || this.isApp ) return // only deal with apps (once)
|
if( !this.data || !this.data.uri || this.isApp ) return // only deal with apps (once)
|
||||||
|
@ -179,7 +179,7 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
createReactiveDOMElement: () => {
|
createReactiveDOMElement: () => {
|
||||||
this.el.dom = document.createElement('div')
|
this.el.dom = document.createElement('div')
|
||||||
this.el.dom.className = this.parseAppURI(this.data.uri).component
|
this.el.dom.className = this.parseAppURI(this.data.uri).component
|
||||||
this.el.dom.innerHTML = this.dom.html(this)
|
this.el.dom.innerHTML = this.com.dom.html(this)
|
||||||
this.el.dom.style.display = 'none'
|
this.el.dom.style.display = 'none'
|
||||||
this.data = reactify( this.dom.el, this.el )
|
this.data = reactify( this.dom.el, this.el )
|
||||||
this.dom.events.map( (e) => this.el.dom.addEventListener(e, (ev) => this.el.emit(e,ev) ) )
|
this.dom.events.map( (e) => this.el.dom.addEventListener(e, (ev) => this.el.emit(e,ev) ) )
|
||||||
|
@ -257,6 +257,9 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
this.isApp = true
|
this.isApp = true
|
||||||
this.el.app = this
|
this.el.app = this
|
||||||
}
|
}
|
||||||
|
return function(){
|
||||||
|
try{ setupApp.apply(this,arguments) }catch(e){ console.error(e) }
|
||||||
|
}
|
||||||
}( AFRAME.AComponent.prototype.updateProperties)
|
}( AFRAME.AComponent.prototype.updateProperties)
|
||||||
|
|
||||||
document.head.innerHTML += `
|
document.head.innerHTML += `
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
/* global AFRAME, THREE */
|
|
||||||
AFRAME.registerComponent('hand-menu-button', {
|
|
||||||
schema: {
|
|
||||||
src: {default: ''},
|
|
||||||
srcHover: {default: ''},
|
|
||||||
mixin: {default: ''}
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this.onWatchButtonHovered = this.onWatchButtonHovered.bind(this);
|
|
||||||
this.onAnimationComplete = this.onAnimationComplete.bind(this);
|
|
||||||
this.onCollisionStarted = this.onCollisionStarted.bind(this);
|
|
||||||
this.onCollisionEnded = this.onCollisionEnded.bind(this);
|
|
||||||
this.onAnimationBegin = this.onAnimationBegin.bind(this);
|
|
||||||
this.onPinchEnded = this.onPinchEnded.bind(this);
|
|
||||||
|
|
||||||
this.el.addEventListener('obbcollisionstarted', this.onCollisionStarted);
|
|
||||||
this.el.addEventListener('obbcollisionended', this.onCollisionEnded);
|
|
||||||
this.el.object3D.renderOrder = 1000;
|
|
||||||
|
|
||||||
this.menuEl = this.el.parentEl;
|
|
||||||
this.handMenuEl = this.el.sceneEl.querySelector('[hand-menu]');
|
|
||||||
|
|
||||||
this.menuEl.addEventListener('animationbegin', this.onAnimationBegin);
|
|
||||||
this.menuEl.addEventListener('animationcomplete', this.onAnimationComplete);
|
|
||||||
},
|
|
||||||
|
|
||||||
onAnimationBegin: function (evt) {
|
|
||||||
// To prevent menu activations while animation is running.
|
|
||||||
if (evt.detail.name === 'animation__open') { this.menuOpen = false; }
|
|
||||||
},
|
|
||||||
|
|
||||||
onAnimationComplete: function (evt) {
|
|
||||||
if (evt.detail.name === 'animation__open') { this.menuOpen = true; }
|
|
||||||
if (evt.detail.name === 'animation__close') { this.menuOpen = false; }
|
|
||||||
},
|
|
||||||
|
|
||||||
onCollisionStarted: function (evt) {
|
|
||||||
var withEl = evt.detail.withEl;
|
|
||||||
if (this.handMenuEl === withEl ||
|
|
||||||
!withEl.components['hand-tracking-controls']) { return; }
|
|
||||||
if (!this.menuOpen) { return; }
|
|
||||||
this.handHoveringEl = withEl;
|
|
||||||
this.el.emit('watchbuttonhoverstarted');
|
|
||||||
},
|
|
||||||
|
|
||||||
onCollisionEnded: function (evt) {
|
|
||||||
var withEl = evt.detail.withEl;
|
|
||||||
if (this.handMenuEl === withEl ||
|
|
||||||
!withEl.components['hand-tracking-controls']) { return; }
|
|
||||||
this.disableHover();
|
|
||||||
this.handHoveringEl = undefined;
|
|
||||||
this.el.emit('watchbuttonhoverended');
|
|
||||||
},
|
|
||||||
|
|
||||||
enableHover: function () {
|
|
||||||
this.handHoveringEl.addEventListener('pinchended', this.onPinchEnded);
|
|
||||||
this.el.setAttribute('material', 'src', this.data.srcHover);
|
|
||||||
},
|
|
||||||
|
|
||||||
disableHover: function () {
|
|
||||||
if (!this.handHoveringEl) { return; }
|
|
||||||
this.handHoveringEl.removeEventListener('pinchended', this.onPinchEnded);
|
|
||||||
this.el.setAttribute('material', 'src', this.data.src);
|
|
||||||
},
|
|
||||||
|
|
||||||
onPinchEnded: (function () {
|
|
||||||
var spawnPosition = new THREE.Vector3(0, 1, 0);
|
|
||||||
return function () {
|
|
||||||
var cubeEl;
|
|
||||||
var newEntityEl;
|
|
||||||
if (!this.menuOpen) { return; }
|
|
||||||
this.menuOpen = false;
|
|
||||||
if (!this.handHoveringEl || !this.data.mixin) { return; }
|
|
||||||
// Spawn shape a little above the menu.
|
|
||||||
spawnPosition.set(0, 1, 0);
|
|
||||||
// Apply rotation of the menu.
|
|
||||||
spawnPosition.applyQuaternion(this.el.parentEl.object3D.quaternion);
|
|
||||||
// 20cm above the menu.
|
|
||||||
spawnPosition.normalize().multiplyScalar(0.2);
|
|
||||||
spawnPosition.add(this.el.parentEl.object3D.position);
|
|
||||||
|
|
||||||
newEntityEl = document.createElement('a-entity');
|
|
||||||
newEntityEl.setAttribute('mixin', this.data.mixin);
|
|
||||||
newEntityEl.setAttribute('position', spawnPosition);
|
|
||||||
this.el.sceneEl.appendChild(newEntityEl);
|
|
||||||
this.handHoveringEl.removeEventListener('pinchended', this.onPinchEnded);
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
|
|
||||||
onWatchButtonHovered: function (evt) {
|
|
||||||
if (evt.target === this.el || !this.handHoveringEl) { return; }
|
|
||||||
this.disableHover();
|
|
||||||
this.handHoveringEl = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
User's hand can collide with multiple buttons simultaneously but only want one in a hovered state.
|
|
||||||
This system keeps track of all the collided buttons, keeping just the closest to the hand in a hovered state.
|
|
||||||
*/
|
|
||||||
AFRAME.registerSystem('hand-menu-button', {
|
|
||||||
init: function () {
|
|
||||||
this.onWatchButtonHovered = this.onWatchButtonHovered.bind(this);
|
|
||||||
this.el.parentEl.addEventListener('watchbuttonhoverended', this.onWatchButtonHovered);
|
|
||||||
this.el.parentEl.addEventListener('watchbuttonhoverstarted', this.onWatchButtonHovered);
|
|
||||||
this.hoveredButtonEls = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
tick: function () {
|
|
||||||
var buttonWorldPosition = new THREE.Vector3();
|
|
||||||
var thumbPosition;
|
|
||||||
var smallestDistance = 1000000;
|
|
||||||
var currentDistance;
|
|
||||||
var closestButtonEl;
|
|
||||||
if (this.hoveredButtonEls.length < 2) { return; }
|
|
||||||
thumbPosition = this.hoveredButtonEls[0].components['hand-menu-button'].handHoveringEl.components['obb-collider'].trackedObject3D.position;
|
|
||||||
for (var i = 0; i < this.hoveredButtonEls.length; ++i) {
|
|
||||||
this.hoveredButtonEls[i].object3D.getWorldPosition(buttonWorldPosition);
|
|
||||||
currentDistance = buttonWorldPosition.distanceTo(thumbPosition);
|
|
||||||
if (currentDistance < smallestDistance) {
|
|
||||||
closestButtonEl = this.hoveredButtonEls[i];
|
|
||||||
smallestDistance = currentDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hoveredButtonEl === closestButtonEl) { return; }
|
|
||||||
|
|
||||||
this.hoveredButtonEl = closestButtonEl;
|
|
||||||
|
|
||||||
for (i = 0; i < this.hoveredButtonEls.length; ++i) {
|
|
||||||
if (!this.hoveredButtonEls[i].components['hand-menu-button'].handHoveringEl) { continue; }
|
|
||||||
if (this.hoveredButtonEls[i] === closestButtonEl) {
|
|
||||||
this.hoveredButtonEls[i].components['hand-menu-button'].enableHover();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.hoveredButtonEls[i].components['hand-menu-button'].disableHover();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onWatchButtonHovered: function (evt) {
|
|
||||||
this.buttonEls = this.el.sceneEl.querySelectorAll('[hand-menu-button]');
|
|
||||||
this.hoveredButtonEls = [];
|
|
||||||
for (var i = 0; i < this.buttonEls.length; ++i) {
|
|
||||||
if (!this.buttonEls[i].components['hand-menu-button'].handHoveringEl) { continue; }
|
|
||||||
this.hoveredButtonEls.push(this.buttonEls[i]);
|
|
||||||
}
|
|
||||||
if (this.hoveredButtonEls.length === 1) {
|
|
||||||
this.hoveredButtonEl = this.hoveredButtonEls[0];
|
|
||||||
this.hoveredButtonEls[0].components['hand-menu-button'].enableHover();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,198 +0,0 @@
|
||||||
/* global AFRAME, THREE */
|
|
||||||
AFRAME.registerComponent('hand-menu', {
|
|
||||||
schema: {
|
|
||||||
location: {default: 'palm', oneOf: ['palm']}
|
|
||||||
},
|
|
||||||
|
|
||||||
menuHTML: /* syntax: html */ `
|
|
||||||
<a-entity
|
|
||||||
scale="0 0 0"
|
|
||||||
animation__open="startEvents: open; dur: 200; property: scale; from: 0 0 1; to: 0.1 0.1 0.1"
|
|
||||||
animation__close="startEvents: close; dur: 200; property: scale; from: 0.1 0.1 0.1; to: 0 0 1">
|
|
||||||
|
|
||||||
<a-entity
|
|
||||||
obb-collider
|
|
||||||
hand-menu-button="src: #infoButton; srcHover: #infoButtonHover"
|
|
||||||
position="0 0 0"
|
|
||||||
rotation="0 180 0"
|
|
||||||
geometry="primitive: plane; width: 1; height: 1;"
|
|
||||||
material="side: double; color: white; shader: flat; src: #infoButton; transparent: true;">
|
|
||||||
</a-entity>
|
|
||||||
|
|
||||||
<a-entity
|
|
||||||
obb-collider
|
|
||||||
hand-menu-button="src: #cubeButton; srcHover: #cubeButtonHover; mixin: cube"
|
|
||||||
position="0 1.12 0"
|
|
||||||
geometry="primitive: plane; width: 1; height: 1;"
|
|
||||||
material="side: double; color: white; shader: flat; src: #cubeButton; transparent: true">
|
|
||||||
</a-entity>
|
|
||||||
|
|
||||||
<a-entity
|
|
||||||
obb-collider
|
|
||||||
hand-menu-button="src: #cylinderButton; srcHover: #cylinderButtonHover; mixin: cylinder"
|
|
||||||
position="0 -1.12 0"
|
|
||||||
geometry="primitive: plane; width: 1; height: 1;"
|
|
||||||
material="side: double; color: white; shader: flat; src: #cylinderButton; transparent: true">
|
|
||||||
</a-entity>
|
|
||||||
|
|
||||||
<a-entity
|
|
||||||
obb-collider
|
|
||||||
hand-menu-button="src: #planeButton; srcHover: #planeButtonHover; mixin: plane"
|
|
||||||
position="-1.12 0 0"
|
|
||||||
geometry="primitive: plane; width: 1; height: 1;"
|
|
||||||
material="side: double; color: white; shader: flat; src: #planeButton; transparent: true">
|
|
||||||
</a-entity>
|
|
||||||
|
|
||||||
<a-entity
|
|
||||||
obb-collider
|
|
||||||
hand-menu-button="src: #sphereButton; srcHover: #sphereButtonHover; mixin: sphere"
|
|
||||||
position="1.12 0 0"
|
|
||||||
geometry="primitive: plane; width: 1; height: 1;"
|
|
||||||
material="side: double; color: white; shader: flat; src: #sphereButton; transparent: true">
|
|
||||||
</a-entity>
|
|
||||||
</a-entity>
|
|
||||||
`,
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this.onCollisionStarted = this.onCollisionStarted.bind(this);
|
|
||||||
this.onCollisionEnded = this.onCollisionEnded.bind(this);
|
|
||||||
this.onSceneLoaded = this.onSceneLoaded.bind(this);
|
|
||||||
this.onEnterVR = this.onEnterVR.bind(this);
|
|
||||||
|
|
||||||
this.throttledOnPinchEvent = AFRAME.utils.throttle(this.throttledOnPinchEvent, 50, this);
|
|
||||||
|
|
||||||
//this.el.sceneEl.addEventListener('loaded', this.onSceneLoaded);
|
|
||||||
this.el.sceneEl.addEventListener('enter-vr', this.onEnterVR);
|
|
||||||
},
|
|
||||||
|
|
||||||
onEnterVR: function () {
|
|
||||||
this.onSceneLoaded()
|
|
||||||
this.setupMenu();
|
|
||||||
},
|
|
||||||
|
|
||||||
onSceneLoaded: function () {
|
|
||||||
var handEls = this.el.sceneEl.querySelectorAll('[hand-tracking-controls]');
|
|
||||||
for (var i = 0; i < handEls.length; i++) {
|
|
||||||
if (handEls[i] === this.el) { continue; }
|
|
||||||
this.handElement = handEls[i];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setupMenu: function () {
|
|
||||||
var template = document.createElement('template');
|
|
||||||
template.innerHTML = this.menuHTML;
|
|
||||||
this.menuEl = template.content.children[0];
|
|
||||||
this.el.sceneEl.appendChild(this.menuEl);
|
|
||||||
|
|
||||||
if (this.data.location === 'palm') {
|
|
||||||
this.setupPalm();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setupPalm: function () {
|
|
||||||
var el = this.openMenuEl = document.createElement('a-entity');
|
|
||||||
el.setAttribute('geometry', 'primitive: circle; radius: 0.025');
|
|
||||||
el.setAttribute('material', 'side: double; src: #palmButton; shader: flat');
|
|
||||||
el.setAttribute('rotation', '90 0 180');
|
|
||||||
el.setAttribute('position', '0 -0.035 -0.07');
|
|
||||||
el.setAttribute('obb-collider', '');
|
|
||||||
el.addEventListener('obbcollisionstarted', this.onCollisionStarted);
|
|
||||||
el.addEventListener('obbcollisionended', this.onCollisionEnded);
|
|
||||||
this.el.appendChild(el);
|
|
||||||
},
|
|
||||||
|
|
||||||
throttledOnPinchEvent: function (evt) {
|
|
||||||
if (evt.type === 'pinchstarted') { this.onPinchStarted(evt); }
|
|
||||||
if (evt.type === 'pinchended') { this.onPinchEnded(evt); }
|
|
||||||
},
|
|
||||||
|
|
||||||
onCollisionStarted: function (evt) {
|
|
||||||
var withEl = evt.detail.withEl;
|
|
||||||
if (this.handElement !== withEl) { return; }
|
|
||||||
withEl.addEventListener('pinchstarted', this.throttledOnPinchEvent);
|
|
||||||
withEl.addEventListener('pinchended', this.throttledOnPinchEvent);
|
|
||||||
this.handHoveringEl = withEl;
|
|
||||||
this.updateUI();
|
|
||||||
},
|
|
||||||
|
|
||||||
onCollisionEnded: function (evt) {
|
|
||||||
var withEl = evt.detail.withEl;
|
|
||||||
if (this.handElement !== withEl) { return; }
|
|
||||||
withEl.removeEventListener('pinchstarted', this.throttledOnPinchEvent);
|
|
||||||
if (!this.opened) {
|
|
||||||
withEl.removeEventListener('pinchended', this.throttledOnPinchEvent);
|
|
||||||
}
|
|
||||||
this.handHoveringEl = undefined;
|
|
||||||
this.updateUI();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateUI: function () {
|
|
||||||
var palmButtonImage;
|
|
||||||
if (this.data.location === 'palm') {
|
|
||||||
palmButtonImage = this.handHoveringEl ? '#palmButtonHover' : '#palmButton';
|
|
||||||
debugger
|
|
||||||
this.openMenuEl.setAttribute('material', 'src', palmButtonImage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onPinchStarted: (function () {
|
|
||||||
var auxMatrix = new THREE.Matrix4();
|
|
||||||
var auxQuaternion = new THREE.Quaternion();
|
|
||||||
return function (evt) {
|
|
||||||
if (!this.handHoveringEl || this.opened) { return; }
|
|
||||||
this.opened = true;
|
|
||||||
this.menuEl.object3D.position.copy(evt.detail.position);
|
|
||||||
this.menuEl.emit('open');
|
|
||||||
function lookAtVector (sourcePoint, destPoint) {
|
|
||||||
return auxQuaternion.setFromRotationMatrix(
|
|
||||||
auxMatrix.identity()
|
|
||||||
.lookAt(sourcePoint, destPoint, new THREE.Vector3(0, 1, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cameraEl = this.el.sceneEl.querySelector('[camera]');
|
|
||||||
var rotationQuaternion = lookAtVector(this.menuEl.object3D.position, cameraEl.object3D.position);
|
|
||||||
this.menuEl.object3D.quaternion.copy(rotationQuaternion);
|
|
||||||
this.pinchedEl = this.handHoveringEl;
|
|
||||||
if (this.data.location === 'palm') { this.openMenuEl.object3D.visible = false; }
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
|
|
||||||
onPinchEnded: function (evt) {
|
|
||||||
if (!this.pinchedEl) { return; }
|
|
||||||
this.opened = false;
|
|
||||||
this.menuEl.emit('close');
|
|
||||||
this.pinchedEl = undefined;
|
|
||||||
this.openMenuEl.object3D.visible = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
lookAtCamera: (function () {
|
|
||||||
var auxVector = new THREE.Vector3();
|
|
||||||
var auxObject3D = new THREE.Object3D();
|
|
||||||
return function (el) {
|
|
||||||
var cameraEl = this.el.sceneEl.querySelector('[camera]');
|
|
||||||
auxVector.subVectors(cameraEl.object3D.position, el.object3D.position).add(el.object3D.position);
|
|
||||||
el.object3D.lookAt(auxVector);
|
|
||||||
el.object3D.rotation.z = 0;
|
|
||||||
};
|
|
||||||
})()
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Watch style UI that work both in VR and AR with @aframevr in one line of <HTML>
|
|
||||||
|
|
||||||
Try now on @Meta Quest Browser
|
|
||||||
|
|
||||||
https://a-watch.glitch.me/
|
|
||||||
|
|
||||||
Just 400 lines of code: https://glitch.com/edit/#!/a-watch
|
|
||||||
|
|
||||||
Watch-style intuitive but easy to occlude hands ⌚️
|
|
||||||
Palm- style less familiar but more robust ✋
|
|
||||||
|
|
||||||
Enjoy! Wanna see more of this? sponsor me on @github
|
|
||||||
|
|
||||||
https://github.com/sponsors/dmarcos
|
|
||||||
|
|
||||||
*/
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* ## dom
|
||||||
|
*
|
||||||
|
* instances reactive DOM component from AFRAME component's `dom` metadata
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <script>
|
||||||
|
* AFRAME.registerComponent('mycom',{
|
||||||
|
* init: function(){ this.data.foo = 1 },
|
||||||
|
* dom: {
|
||||||
|
* scale: 3,
|
||||||
|
* events: ['click'],
|
||||||
|
* html: (me) => `<div id="iconmenu">${me.data.foo}</div>`,
|
||||||
|
* css: (me) => `#iconmenu { display:block }`
|
||||||
|
* },
|
||||||
|
* event: {
|
||||||
|
* click: (e) => alert(e),
|
||||||
|
* foo: (e) => alert("I was updated!")
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* </script>
|
||||||
|
*
|
||||||
|
* <a-entity mycom dom/>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* | property | type | example |
|
||||||
|
* |--------------|--------------------|----------------------------------------------------------------------------------------|
|
||||||
|
* | `com` | `array` of strings | <a-entity app="app/launcher.js; registers: https://foo.com/index.json, ./index.json"/> |
|
||||||
|
*
|
||||||
|
* | event | target | info |
|
||||||
|
* |--------------|-------------------------------------------------------------------------------------------------------------|
|
||||||
|
* | `DOMready` | self | fired when dom component (`this.dom`) is created |
|
||||||
|
*/
|
||||||
|
|
||||||
|
AFRAME.registerComponent('dom',{
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
Object.values(this.el.components)
|
||||||
|
.map( (c) => {
|
||||||
|
if( c.dom && c.attrName != "dom"){
|
||||||
|
this.dom = c.dom
|
||||||
|
this.com = c
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if( !this.dom ) return console.warn('dom.js did not find a .dom object inside components')
|
||||||
|
|
||||||
|
this
|
||||||
|
.ensureOverlay()
|
||||||
|
.addCSS()
|
||||||
|
.createReactiveDOMElement()
|
||||||
|
.scaleDOMvsXR()
|
||||||
|
.triggerKeyboardForInputs()
|
||||||
|
.setupListeners()
|
||||||
|
|
||||||
|
document.querySelector('#overlay').appendChild(this.el.dom)
|
||||||
|
this.el.emit('DOMready',{el: this.el.dom})
|
||||||
|
},
|
||||||
|
|
||||||
|
ensureOverlay(){
|
||||||
|
// ensure overlay
|
||||||
|
let overlay = document.querySelector('#overlay')
|
||||||
|
if( !overlay ){
|
||||||
|
overlay = document.createElement('div')
|
||||||
|
overlay.id = "overlay"
|
||||||
|
document.body.appendChild(overlay)
|
||||||
|
document.querySelector("a-scene").setAttribute("webxr","overlayElement:#overlay")
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
reactify: function(el,data){
|
||||||
|
return new Proxy(data, {
|
||||||
|
get(me,k,v) {
|
||||||
|
return me[k]
|
||||||
|
},
|
||||||
|
set(me,k,v){
|
||||||
|
me[k] = v
|
||||||
|
el.emit(k,{el,k,v})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// creates el.dom (the 2D DOM object)
|
||||||
|
createReactiveDOMElement: function(){
|
||||||
|
this.el.dom = document.createElement('div')
|
||||||
|
this.el.dom.innerHTML = this.dom.html(this)
|
||||||
|
this.el.dom.className = this.dom.attrName
|
||||||
|
this.com.data = this.reactify( this.el, this.com.data )
|
||||||
|
if( this.dom.events ) this.dom.events.map( (e) => this.el.dom.addEventListener(e, (ev) => this.el.emit(e,ev) ) )
|
||||||
|
this.el.dom = this.el.dom.children[0]
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
addCSS: function(){
|
||||||
|
if( this.dom.css && !document.head.querySelector(`style#${this.attrName}`) ){
|
||||||
|
document.head.innerHTML += `<style id="${this.attrName}">${this.dom.css(this)}</style>`
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
scaleDOMvsXR: function(){
|
||||||
|
if( this.dom.scale ) this.el.setAttribute('scale',`${this.dom.scale} ${this.dom.scale} ${this.dom.scale}`)
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
setupListeners: function(){
|
||||||
|
this.el.sceneEl.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
|
||||||
|
this.el.sceneEl.addEventListener('apps:XR', () => {
|
||||||
|
this.el.setAttribute('visible', true)
|
||||||
|
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
triggerKeyboardForInputs: function(){
|
||||||
|
// https://developer.oculus.com/documentation/web/webxr-keyboard ;
|
||||||
|
[...this.el.dom.querySelectorAll('[type=text]')].map( (input) => {
|
||||||
|
let triggerKeyboard = function(){
|
||||||
|
this.focus()
|
||||||
|
console.log("focus")
|
||||||
|
}
|
||||||
|
input.addEventListener('click', triggerKeyboard )
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
|
@ -3,12 +3,10 @@ AFRAME.registerComponent('helloworld-window', {
|
||||||
foo: { type:"string"}
|
foo: { type:"string"}
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function(){},
|
dependencies: ['dom'],
|
||||||
|
|
||||||
requires:{
|
init: function(){
|
||||||
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
this.el.object3D.visible = false
|
||||||
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
|
|
||||||
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", //
|
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
|
@ -23,9 +21,6 @@ AFRAME.registerComponent('helloworld-window', {
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// component events
|
|
||||||
html: function( ){ console.log("html-mesh requirement mounted") },
|
|
||||||
|
|
||||||
// combined AFRAME+DOM reactive events
|
// combined AFRAME+DOM reactive events
|
||||||
click: function(e){ }, //
|
click: function(e){ }, //
|
||||||
keydown: function(e){ }, //
|
keydown: function(e){ }, //
|
||||||
|
@ -33,14 +28,16 @@ AFRAME.registerComponent('helloworld-window', {
|
||||||
// reactive events for this.data updates
|
// reactive events for this.data updates
|
||||||
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
||||||
|
|
||||||
ready: function( ){
|
launcher: async function(){
|
||||||
this.el.dom.style.display = 'none'
|
this.el.dom.style.display = 'none'
|
||||||
console.log("this.el.dom has been added to DOM")
|
await AFRAME.utils.require({
|
||||||
|
dom: "./com/dom.js", // interpret .dom object
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", //
|
||||||
|
})
|
||||||
this.data.myvalue = 1
|
this.data.myvalue = 1
|
||||||
setInterval( () => this.data.myvalue++, 100 )
|
setInterval( () => this.data.myvalue++, 100 )
|
||||||
},
|
|
||||||
|
|
||||||
launcher: function(){
|
|
||||||
console.log("this.el.dom has been added to DOM")
|
console.log("this.el.dom has been added to DOM")
|
||||||
new WinBox("Hello World",{
|
new WinBox("Hello World",{
|
||||||
width: 250,
|
width: 250,
|
||||||
|
@ -58,8 +55,8 @@ AFRAME.registerComponent('helloworld-window', {
|
||||||
},
|
},
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
"short_name": "Hello world",
|
"short_name": "Hello world window",
|
||||||
"name": "Hello world",
|
"name": "Hello world window",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "https://css.gg/browser.svg",
|
"src": "https://css.gg/browser.svg",
|
|
@ -1,8 +1,11 @@
|
||||||
AFRAME.registerComponent('helloworld', {
|
AFRAME.registerComponent('helloworld', {
|
||||||
schema: {
|
schema: {
|
||||||
foo: { type:"string"}
|
wireframe: { type:"boolean", "default":false},
|
||||||
|
foo: {type:"string","default":"foo"}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dependencies:['dom'],
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
this.el.object3D.visible = false
|
this.el.object3D.visible = false
|
||||||
|
|
||||||
|
@ -12,22 +15,15 @@ AFRAME.registerComponent('helloworld', {
|
||||||
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
|
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
|
||||||
<a-entity position="0 1.8 -3" scale="10 10 10" text="value: ${this.data.foo}; align:center; color:#888"></a-entity>
|
<a-entity position="0 1.8 -3" scale="10 10 10" text="value: ${this.data.foo}; align:center; color:#888"></a-entity>
|
||||||
`
|
`
|
||||||
|
|
||||||
this.interval = setInterval( () => this.data.wireframe = !this.data.wireframe, 500 )
|
|
||||||
},
|
|
||||||
|
|
||||||
requires:{
|
|
||||||
// somecomponent: "https://unpkg.com/some-aframe-component/mycom.min.js"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// component events
|
|
||||||
somecomponent: function( ){ console.log("component requirement mounted") },
|
|
||||||
ready: function(e){ console.log("requires are loaded") },
|
|
||||||
|
|
||||||
launcher: function(e){
|
launcher: function(e){
|
||||||
this.el.object3D.visible = !this.el.object3D.visible
|
this.el.object3D.visible = !this.el.object3D.visible
|
||||||
|
this.interval = setInterval( () => {
|
||||||
|
this.data.wireframe = !this.data.wireframe
|
||||||
|
}, 500 )
|
||||||
},
|
},
|
||||||
|
|
||||||
// reactive this.data value demo
|
// reactive this.data value demo
|
|
@ -18,30 +18,29 @@
|
||||||
|
|
||||||
AFRAME.registerComponent('launcher', {
|
AFRAME.registerComponent('launcher', {
|
||||||
schema: {
|
schema: {
|
||||||
foo: { type:"string"}
|
attach: { type:"selector"}
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function () {
|
dependencies:['dom'],
|
||||||
|
|
||||||
|
init: async function () {
|
||||||
this.data.apps = []
|
this.data.apps = []
|
||||||
|
|
||||||
AFRAME.scenes.map( (scene) => {
|
await AFRAME.utils.require({
|
||||||
scene.addEventListener('app:ready', (e) => this.render(e.detail) )
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
dom: "./com/dom.js",
|
||||||
|
svgfile: "https://7dir.github.io/aframe-svgfile-component/aframe-svgfile-component.min.js",
|
||||||
})
|
})
|
||||||
},
|
|
||||||
|
|
||||||
requires:{
|
this.el.setAttribute("dom","")
|
||||||
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
this.render()
|
||||||
'hand-menu': "./com/control/hand-menu.js",
|
this.el.sceneEl.addEventListener('enter-vr', (e) => this.render() )
|
||||||
'hand-menu-button': "./com/control/hand-menu-button.js",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
scale: 3,
|
scale: 3,
|
||||||
events: ['click'],
|
events: ['click'],
|
||||||
html: (me) => `<div>
|
html: (me) => `<div id="iconmenu">loading components..</div>`,
|
||||||
<div id="iconmenu"></div>
|
|
||||||
</div>`,
|
|
||||||
|
|
||||||
css: (me) => `#iconmenu {
|
css: (me) => `#iconmenu {
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -102,47 +101,67 @@ AFRAME.registerComponent('launcher', {
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// combined AFRAME+DOM reactive events
|
|
||||||
click: function(e){
|
|
||||||
console.dir(e)
|
|
||||||
}, //
|
|
||||||
|
|
||||||
|
|
||||||
ready: function( ){
|
|
||||||
this.el.dom.children[0].id = this.el.uid // important hint for html-mesh
|
|
||||||
document.querySelector('#left-hand').setAttribute('hand-menu','')
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function(app){
|
render: async function(){
|
||||||
clearTimeout(this.timeout)
|
if( !this.el.dom ) return // too early (dom.js component not ready)
|
||||||
this.timeout = setTimeout( () => {
|
|
||||||
const drawButton = (app) => {
|
let inVR = this.sceneEl && this.sceneEl.renderer.xr.isPresenting
|
||||||
if( !app.manifest ) return
|
let items = [...this.el.children]
|
||||||
console.log(app.data.uri+" "+app.data.order)
|
let requires = []
|
||||||
let btn = app.btn = document.createElement('button')
|
let i = 0
|
||||||
if( app.manifest.icons?.length > 0){
|
let colors = [
|
||||||
let img = document.createElement('img')
|
'#4C73FE',
|
||||||
img.src = app.manifest.icons[0].src
|
'#554CFE',
|
||||||
img.title = app.manifest.name + ": " + app.manifest.description
|
'#864CFE',
|
||||||
btn.appendChild(img)
|
'#B44CFE',
|
||||||
}else btn.innerText = app.manifest.short_name
|
'#E24CFE',
|
||||||
btn.addEventListener('click', () => {
|
'#FE4CD3'
|
||||||
app.el.emit('launcher',app)
|
]
|
||||||
app.el.sceneEl.emit('launched',app)
|
|
||||||
})
|
const add2D = (launchCom,el,manifest,aentity) => {
|
||||||
this.el.dom.querySelector('#iconmenu').appendChild(btn)
|
let btn = document.createElement('button')
|
||||||
|
btn.innerHTML = `${ manifest?.icons?.length > 0
|
||||||
|
? `<img src='${manifest.icons[0].src}' title='${manifest.name}: ${manifest.description}'/>`
|
||||||
|
: `${manifest.short_name}`
|
||||||
|
}`
|
||||||
|
btn.addEventListener('click', () => el.emit('launcher',{}) )
|
||||||
|
this.el.dom.appendChild(btn)
|
||||||
|
}
|
||||||
|
|
||||||
|
const add3D = (launchCom,el,manifest) => {
|
||||||
|
let aentity = document.createElement('a-entity')
|
||||||
|
let atext = document.createElement('a-entity')
|
||||||
|
aentity.setAttribute("mixin","menuitem")
|
||||||
|
aentity.setAttribute("position",`${i++ * 0.2} 0 0`)
|
||||||
|
if( !aentity.getAttribute("material")){
|
||||||
|
aentity.setAttribute('material',`side: double; color: ${colors[ i % colors.length]}`)
|
||||||
}
|
}
|
||||||
|
atext.setAttribute("text",`value: ${manifest.short_name}; align: baseline; anchor: align; align:center; wrapCount:7`)
|
||||||
|
atext.setAttribute("scale","0.1 0.1 0.1")
|
||||||
|
atext.setAttribute("position","0 0 0.0")
|
||||||
|
aentity.appendChild(atext)
|
||||||
|
this.el.appendChild(aentity)
|
||||||
|
return aentity
|
||||||
|
}
|
||||||
|
|
||||||
const drawCategory = (app,c) => app.manifest &&
|
// finally render them!
|
||||||
app.manifest.short_name != "launcher" &&
|
this.el.dom.innerHTML = '' // clear
|
||||||
app.manifest.category == c ? drawButton(app) : false
|
this.system.components.map( (c) => {
|
||||||
|
const launchComponentKey = c.getAttributeNames().pop()
|
||||||
|
const launchCom = c.components[ launchComponentKey ]
|
||||||
|
if( !launchCom ) return console.warn(`could not find component '${launchComponentKey}' (forgot to include script-tag?)`)
|
||||||
|
const manifest = launchCom.manifest
|
||||||
|
if( manifest ){
|
||||||
|
add2D(launchCom,c,manifest, add3D(launchCom,c,manifest) )
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
AFRAME.app.foreach( (app) => drawCategory(app,undefined) )
|
if( this.data.attach ){
|
||||||
AFRAME.app.foreach( (app) => drawCategory(app,"system") )
|
this.el.object3D.visible = inVR ? true : false
|
||||||
|
// if( inVR ) this.data.attach.appendChild(this.el)
|
||||||
|
}
|
||||||
|
|
||||||
},100)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
@ -199,3 +218,37 @@ in above's case "\nHelloworld application\n" will qualify as header.
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
AFRAME.registerSystem('launcher',{
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
this.components = []
|
||||||
|
// observer HTML changes in <a-scene>
|
||||||
|
observer = new MutationObserver( (a,b) => this.getLaunchables(a,b) )
|
||||||
|
observer.observe( this.sceneEl, {characterData: false, childList: true, attributes: false});
|
||||||
|
},
|
||||||
|
|
||||||
|
getLaunchables: function(mutationsList,observer){
|
||||||
|
let searchEvent = 'launcher'
|
||||||
|
let els = [...this.sceneEl.getElementsByTagName("*")]
|
||||||
|
|
||||||
|
this.components = els.filter( (el) => {
|
||||||
|
let hasEvent = false
|
||||||
|
if( el.components ){
|
||||||
|
for( let i in el.components ){
|
||||||
|
if( el.components[i].events && el.components[i].events[searchEvent] ){
|
||||||
|
hasEvent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasEvent ? el : null
|
||||||
|
})
|
||||||
|
this.updateLauncher()
|
||||||
|
},
|
||||||
|
|
||||||
|
updateLauncher: function(){
|
||||||
|
let launcher = document.querySelector('[launcher]')
|
||||||
|
if( launcher ) launcher.components['launcher'].render()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
|
@ -1,86 +0,0 @@
|
||||||
AFRAME.registerComponent('osbutton',{
|
|
||||||
|
|
||||||
data:{
|
|
||||||
width: {type: 'number', default: 0.4},
|
|
||||||
height: {type: 'number', default: 0.2},
|
|
||||||
depth: {type: 'number', default: 0.06},
|
|
||||||
color: {type: 'color', default: 'blue'},
|
|
||||||
distance: {type: 'number'},
|
|
||||||
label: {type: 'string'}
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(){
|
|
||||||
this
|
|
||||||
.createBox()
|
|
||||||
.setupDistanceTick()
|
|
||||||
},
|
|
||||||
|
|
||||||
setupDistanceTick: function(){
|
|
||||||
// we throttle by distance, to support scenes with loads of clickable objects (far away)
|
|
||||||
if( !this.data.distance ) this.data.distance = 0.9
|
|
||||||
this.distance = -1
|
|
||||||
this.worldPosition = new THREE.Vector3()
|
|
||||||
this.posCam = new THREE.Vector3()
|
|
||||||
this.tick = this.throttleByDistance( () => this.showSource() )
|
|
||||||
},
|
|
||||||
|
|
||||||
createBox: function(){
|
|
||||||
let geometry = this.geometry = new THREE.BoxGeometry(this.data.width, this.data.height, this.data.depth);
|
|
||||||
this.material = new THREE.MeshStandardMaterial({color: this.data.color });
|
|
||||||
this.mesh = new THREE.Mesh(this.geometry, this.material);
|
|
||||||
this.scaleChildToButton(this.el.object3D, this.mesh)
|
|
||||||
this.el.object3D.add(this.mesh)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
throttleByDistance: function(f){
|
|
||||||
return function(){
|
|
||||||
if( this.distance < 0 ) return f() // first call
|
|
||||||
if( !f.tid ){
|
|
||||||
let x = this.distance
|
|
||||||
let y = x*(x*0.05)*1000 // parabolic curve
|
|
||||||
f.tid = setTimeout( function(){
|
|
||||||
f.tid = null
|
|
||||||
f()
|
|
||||||
}, y )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showSource: function(){
|
|
||||||
this.el.sceneEl.camera.getWorldPosition(this.posCam)
|
|
||||||
this.el.object3D.getWorldPosition(this.worldPosition)
|
|
||||||
this.distance = this.posCam.distanceTo(this.worldPosition)
|
|
||||||
|
|
||||||
if( this.distance < this.data.distance ){
|
|
||||||
this.material.side = THREE.BackSide
|
|
||||||
}else{
|
|
||||||
this.material.side = THREE.FrontSide
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
scaleChildToButton: function(scene, mesh ){
|
|
||||||
let cleanScene = scene.clone()
|
|
||||||
let remove = []
|
|
||||||
const notVisible = (n) => !n.visible || (n.material && !n.material.visible)
|
|
||||||
cleanScene.traverse( (n) => notVisible(n) && n.children.length == 0 && (remove.push(n)) )
|
|
||||||
remove.map( (n) => n.removeFromParent() )
|
|
||||||
let restrictTo3DBoundingBox = mesh.geometry
|
|
||||||
if( restrictTo3DBoundingBox ){
|
|
||||||
// normalize instanced objectsize to boundingbox
|
|
||||||
let sizeFrom = new THREE.Vector3()
|
|
||||||
let sizeTo = new THREE.Vector3()
|
|
||||||
let empty = new THREE.Object3D()
|
|
||||||
new THREE.Box3().setFromObject(mesh).getSize(sizeTo)
|
|
||||||
new THREE.Box3().setFromObject(cleanScene).getSize(sizeFrom)
|
|
||||||
let ratio = sizeFrom.divide(sizeTo)
|
|
||||||
scene.children.map( (c) => {
|
|
||||||
if( c.uuid != mesh.uuid ){
|
|
||||||
c.scale.multiplyScalar( 1.0 / Math.max(ratio.x, ratio.y, ratio.z))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
})
|
|
|
@ -9,21 +9,12 @@ AFRAME.registerComponent('save', {
|
||||||
//this.el.innerHTML = ` `
|
//this.el.innerHTML = ` `
|
||||||
},
|
},
|
||||||
|
|
||||||
requires:{
|
|
||||||
// somecomponent: "https://unpkg.com/some-aframe-component/mycom.min.js"
|
|
||||||
},
|
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// component events
|
|
||||||
somecomponent: function( ){ console.log("component requirement mounted") },
|
|
||||||
ready: function(e){ console.log("requires are loaded") },
|
|
||||||
|
|
||||||
launcher: function(e){
|
launcher: function(e){
|
||||||
this.save()
|
this.save()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
save: function(){
|
save: function(){
|
|
@ -3,30 +3,33 @@ AFRAME.registerComponent('xrfragments', {
|
||||||
url: { type:"string"}
|
url: { type:"string"}
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function () {
|
dependencies:['dom'],
|
||||||
},
|
|
||||||
|
|
||||||
requires:{
|
init: function () {
|
||||||
xrfragments: "https://xrfragment.org/dist/xrfragment.aframe.js",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// requires are loaded
|
launcher: async function(){
|
||||||
ready: function(e){
|
|
||||||
this.el.setAttribute("xrf","https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
|
|
||||||
|
|
||||||
let ARbutton = document.querySelector('.a-enter-ar-button')
|
|
||||||
if( ARbutton ){
|
|
||||||
ARbutton.addEventListener('click', () => {
|
|
||||||
AFRAME.XRF.reset()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
launcher: function(){
|
|
||||||
let url = prompt('enter URL to glb/fbx/json/obj/usdz asset', 'https://xrfragment.org/index.glb')
|
let url = prompt('enter URL to glb/fbx/json/obj/usdz asset', 'https://xrfragment.org/index.glb')
|
||||||
if( url ) AFRAME.XRF.navigator.to(url)
|
if( !url ) return
|
||||||
|
await AFRAME.utils.require({
|
||||||
|
xrfragments: "https://xrfragment.org/dist/xrfragment.aframe.js",
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove objects which are marked to be removed from scene (with noxrf)
|
||||||
|
let els = [...document.querySelectorAll('[noxrf]') ]
|
||||||
|
els.map( (el) => el.remove() )
|
||||||
|
|
||||||
|
if( !this.el.getAttribute("xrf") ){
|
||||||
|
this.el.setAttribute("xrf", url )
|
||||||
|
let ARbutton = document.querySelector('.a-enter-ar-button')
|
||||||
|
if( ARbutton ){
|
||||||
|
ARbutton.addEventListener('click', () => {
|
||||||
|
AFRAME.XRF.reset()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}else AFRAME.XRF.navigator.to(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
Loading…
Reference in New Issue