xrsh-com/com/control/hand-menu.js

199 lines
6.8 KiB
JavaScript
Raw Normal View History

2024-05-22 12:56:43 +02:00
/* 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
*/