81 lines
2.9 KiB
JavaScript
81 lines
2.9 KiB
JavaScript
// this makes WebXR hand controls able to click things (by touching it)
|
|
|
|
AFRAME.registerComponent('pressable', {
|
|
schema: {
|
|
pressDistance: { default: 0.005 },
|
|
pressDuration: { default: 300 },
|
|
immersiveOnly: { default: true }
|
|
},
|
|
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) => {
|
|
if( !this.data || (this.data.immersiveOnly && !this.el.sceneEl.renderer.xr.isPresenting) ) return
|
|
this.el.emit('click', e.detail )
|
|
})
|
|
|
|
},
|
|
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(){
|
|
if( this.handEls.length == 0 ){
|
|
this.handEls = document.querySelectorAll('[hand-tracking-controls]');
|
|
}
|
|
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
|
|
if( !object3D ) return
|
|
|
|
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
|
|
},
|
|
|
|
});
|