73 lines
2.7 KiB
JavaScript
73 lines
2.7 KiB
JavaScript
// this makes WebXR hand controls able to click things (by touching it)
|
|
|
|
AFRAME.registerComponent('pressable', {
|
|
schema: {
|
|
pressDistance: {
|
|
default: 0.01
|
|
}
|
|
},
|
|
init: function() {
|
|
this.worldPosition = new THREE.Vector3();
|
|
this.fingerWorldPosition = 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() )
|
|
},
|
|
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 xrf-get AFRAME component (which references non-reparented buffergeometries from the 3D model)
|
|
let object3D = this.el.object3D.child || this.el.object3D
|
|
|
|
for (var i = 0; i < handEls.length; i++) {
|
|
handEl = handEls[i];
|
|
let indexTipPosition = handEl.components['hand-tracking-controls'].indexTipPosition
|
|
// Apply the relative position to the parent's world position
|
|
handEl.object3D.updateMatrixWorld();
|
|
handEl.object3D.getWorldPosition( this.fingerWorldPosition )
|
|
this.fingerWorldPosition.add( indexTipPosition )
|
|
|
|
this.raycaster.far = this.data.pressDistance
|
|
// Create a direction vector (doesnt matter because it is supershort for 'touch' purposes)
|
|
const direction = new THREE.Vector3(1.0,0,0);
|
|
this.raycaster.set(this.fingerWorldPosition, direction)
|
|
intersects = this.raycaster.intersectObjects([object3D],true)
|
|
|
|
object3D.getWorldPosition(this.worldPosition)
|
|
|
|
distance = this.fingerWorldPosition.distanceTo(this.worldPosition)
|
|
minDistance = distance < minDistance ? distance : minDistance
|
|
|
|
if (intersects.length ){
|
|
if( !this.pressed ){
|
|
this.el.emit('pressedstarted');
|
|
this.el.emit('click');
|
|
this.pressed = setTimeout( () => {
|
|
this.el.emit('pressedended');
|
|
this.pressed = null
|
|
},300)
|
|
}
|
|
}
|
|
}
|
|
this.distance = minDistance
|
|
}
|
|
});
|