// this makes WebXR hand controls able to click things (by touching it) AFRAME.registerComponent('pressable', { schema: { pressDistance: { default: 0.003 }, pressDuration: { default: 300 } }, init: function() { this.worldPosition = new THREE.Vector3(); this.raycaster = new THREE.Raycaster() this.handEls = document.querySelectorAll('[hand-tracking-controls]'); this.pressed = false; this.distance = -1 // we throttle by distance, to support scenes with loads of clickable objects (far away) this.tick = this.throttleByDistance( () => this.detectPress() ) this.el.addEventListener("raycaster-intersected", (e) => this.el.emit('click', e.detail ) ) this.el.addEventListener('click', (e) => { document.querySelector('[isoterminal]').components.isoterminal.term.term.write("\r\nclick") }) }, 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 ) } } }, detectPress: function(){ var handEls = this.handEls; var handEl; let minDistance = 5 // compensate for an object inside a group let object3D = this.el.object3D.type == "Group" ? this.el.object3D.children[0] : this.el.object3D for (var i = 0; i < handEls.length; i++) { handEl = handEls[i]; let indexTip = handEl.object3D.getObjectByName('index-finger-tip') if( ! indexTip ) return // nothing to do here this.raycaster.far = this.data.pressDistance // Create a direction vector to negative Z const direction = new THREE.Vector3(0,0,-1.0); direction.normalize() this.raycaster.set(indexTip.position, direction) intersects = this.raycaster.intersectObjects([object3D],true) object3D.getWorldPosition(this.worldPosition) distance = indexTip.position.distanceTo(this.worldPosition) minDistance = distance < minDistance ? distance : minDistance if (intersects.length ){ this.i = this.i || 0; if( !this.pressed ){ this.el.emit('pressedstarted', intersects); this.el.emit('click', intersects); this.pressed = setTimeout( () => { this.el.emit('pressedended', intersects); this.pressed = null }, this.data.pressDuration ) } } } this.distance = minDistance }, });