added controller things
This commit is contained in:
parent
375f30ef70
commit
a2a26dc739
|
@ -0,0 +1,47 @@
|
||||||
|
// gaze + fuse-to-click on mobile VR
|
||||||
|
// gaze + tap-to-click on mobile AR
|
||||||
|
// also adds 'eye'-tracking (mouseover) functionality to display popovers
|
||||||
|
|
||||||
|
AFRAME.registerComponent('gaze-touch-to-click',{
|
||||||
|
schema:{
|
||||||
|
spawn:{type:'boolean',default:false},
|
||||||
|
},
|
||||||
|
events:{
|
||||||
|
"fusing": function(e){
|
||||||
|
if( e.detail.mouseEvent ) return // ignore click event
|
||||||
|
console.dir(e)
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setGazer: function(state, fuse){
|
||||||
|
if( !AFRAME.utils.device.isMobile() ) return
|
||||||
|
let cam = document.querySelector("[camera]")
|
||||||
|
if( state ){
|
||||||
|
if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls
|
||||||
|
cam.innerHTML = `<a-entity id="cursor" cursor="fuse: ${fuse ? 'true': 'false'}; fuseTimeout: 1500"
|
||||||
|
animation__click="property: scale; startEvents: click; easing: easeInCubic; dur: 150; from: 0.1 0.1 0.1; to: 1 1 1"
|
||||||
|
animation__fusing="property: scale; startEvents: fusing; easing: easeInCubic; dur: 1500; from: 1 1 1; to: 0.1 0.1 0.1"
|
||||||
|
animation__mouseleave="property: scale; startEvents: mouseleave; easing: easeInCubic; dur: 500; to: 1 1 1"
|
||||||
|
raycaster="objects: .ray"
|
||||||
|
visible="true"
|
||||||
|
position="0 0 -1"
|
||||||
|
material="color: #BBBBBB; shader: flat">
|
||||||
|
</a-entity>`
|
||||||
|
cam.querySelector('#cursor').setAttribute("geometry","primitive: ring; radiusInner: 0.02; radiusOuter: 0.03")
|
||||||
|
}else{
|
||||||
|
cam.querySelector('#cursor').removeAttribute("geometry")
|
||||||
|
if( document.querySelector('[cursor]') ) {
|
||||||
|
document.querySelector('[cursor]').setAttribute("visible",false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init:function(data){
|
||||||
|
this.setGazer(true);
|
||||||
|
|
||||||
|
document.querySelector("a-scene").addEventListener('exit-vr', () => this.setGazer(false,false) )
|
||||||
|
document.querySelector("a-scene").addEventListener('enter-vr', () => this.setGazer(true,true) )
|
||||||
|
document.querySelector("a-scene").addEventListener('enter-ar', () => this.setGazer(true,false) )
|
||||||
|
document.querySelector("a-scene").addEventListener('exit-ar', () => this.setGazer(false,false) )
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,67 @@
|
||||||
|
// look-controls turns off autoUpdateMatrix (of player) which
|
||||||
|
// will break repositionining the VR rig (teleporting and other stuff)
|
||||||
|
// overriding this below is easier then adding updateMatrixWorld() everywhere else
|
||||||
|
// (or rolling your own look-controls and diverting from mainbranch)
|
||||||
|
|
||||||
|
AFRAME.registerComponent('patch-look-controls',{
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
alert("fjo"); //dEventListener('loaded', () => this.patchLookControls() )
|
||||||
|
},
|
||||||
|
|
||||||
|
patchLookControls: function(){
|
||||||
|
alert("ja!")
|
||||||
|
let lk = document.querySelector('[look-controls]')
|
||||||
|
if( !lk ) return
|
||||||
|
lk = lk.components['look-controls']
|
||||||
|
|
||||||
|
lk.onEnterVR = function () {
|
||||||
|
var sceneEl = this.el.sceneEl;
|
||||||
|
if (!sceneEl.checkHeadsetConnected()) { return; }
|
||||||
|
this.saveCameraPose();
|
||||||
|
this.el.object3D.position.set(0, 0, 0);
|
||||||
|
this.el.object3D.rotation.set(0, 0, 0);
|
||||||
|
if (sceneEl.hasWebXR) {
|
||||||
|
// this.el.object3D.matrixAutoUpdate = false;
|
||||||
|
this.el.object3D.updateMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the pose.
|
||||||
|
*/
|
||||||
|
lk.onExitVR = function () {
|
||||||
|
if (!this.el.sceneEl.checkHeadsetConnected()) { return; }
|
||||||
|
this.restoreCameraPose();
|
||||||
|
this.previousHMDPosition.set(0, 0, 0);
|
||||||
|
this.el.object3D.matrixAutoUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it also needs to apply the offset (in case the #rot was used in URLS)
|
||||||
|
|
||||||
|
lk.updateOrientation = function () {
|
||||||
|
var object3D = this.el.object3D;
|
||||||
|
var pitchObject = this.pitchObject;
|
||||||
|
var yawObject = this.yawObject;
|
||||||
|
var sceneEl = this.el.sceneEl;
|
||||||
|
|
||||||
|
// In VR or AR mode, THREE is in charge of updating the camera pose.
|
||||||
|
if ((sceneEl.is('vr-mode') || sceneEl.is('ar-mode')) && sceneEl.checkHeadsetConnected()) {
|
||||||
|
// With WebXR THREE applies headset pose to the object3D internally.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateMagicWindowOrientation();
|
||||||
|
|
||||||
|
let offsetX = object3D.rotation.offset ? object3D.rotation.offset.x : 0
|
||||||
|
let offsetY = object3D.rotation.offset ? object3D.rotation.offset.y : 0
|
||||||
|
|
||||||
|
// On mobile, do camera rotation with touch events and sensors.
|
||||||
|
object3D.rotation.x = this.magicWindowDeltaEuler.x + offsetX + pitchObject.rotation.x;
|
||||||
|
object3D.rotation.y = this.magicWindowDeltaEuler.y + offsetY + yawObject.rotation.y;
|
||||||
|
object3D.rotation.z = this.magicWindowDeltaEuler.z;
|
||||||
|
object3D.matrixAutoUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,65 @@
|
||||||
|
// look-controls turns off autoUpdateMatrix (of player) which
|
||||||
|
// will break repositionining the VR rig (teleporting and other stuff)
|
||||||
|
// overriding this below is easier then adding updateMatrixWorld() everywhere else
|
||||||
|
// (or rolling your own look-controls and diverting from mainbranch)
|
||||||
|
|
||||||
|
|
||||||
|
// this component will re-attach a patched look-controls
|
||||||
|
AFRAME.registerComponent('patch-look-controls',{
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
let scene = AFRAME.scenes[0]
|
||||||
|
let el = document.querySelector('[look-controls]')
|
||||||
|
el.removeAttribute("look-controls")
|
||||||
|
|
||||||
|
AFRAME.components['look-controls'].Component.prototype.onEnterVR = function () {
|
||||||
|
var sceneEl = this.el.sceneEl;
|
||||||
|
if (!sceneEl.checkHeadsetConnected()) { return; }
|
||||||
|
this.saveCameraPose();
|
||||||
|
this.el.object3D.position.set(0, 0, 0);
|
||||||
|
this.el.object3D.rotation.set(0, 0, 0);
|
||||||
|
if (sceneEl.hasWebXR) {
|
||||||
|
// this.el.object3D.matrixAutoUpdate = false;
|
||||||
|
this.el.object3D.updateMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the pose.
|
||||||
|
*/
|
||||||
|
AFRAME.components['look-controls'].Component.prototype.onExitVR = function () {
|
||||||
|
if (!this.el.sceneEl.checkHeadsetConnected()) { return; }
|
||||||
|
this.restoreCameraPose();
|
||||||
|
this.previousHMDPosition.set(0, 0, 0);
|
||||||
|
this.el.object3D.matrixAutoUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it also needs to apply the offset (in case the #rot was used in URLS)
|
||||||
|
|
||||||
|
AFRAME.components['look-controls'].Component.prototype.updateOrientation = function () {
|
||||||
|
var object3D = this.el.object3D;
|
||||||
|
var pitchObject = this.pitchObject;
|
||||||
|
var yawObject = this.yawObject;
|
||||||
|
var sceneEl = this.el.sceneEl;
|
||||||
|
|
||||||
|
// In VR or AR mode, THREE is in charge of updating the camera pose.
|
||||||
|
if ((sceneEl.is('vr-mode') || sceneEl.is('ar-mode')) && sceneEl.checkHeadsetConnected()) {
|
||||||
|
// With WebXR THREE applies headset pose to the object3D internally.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateMagicWindowOrientation();
|
||||||
|
|
||||||
|
let offsetX = object3D.rotation.offset ? object3D.rotation.offset.x : 0
|
||||||
|
let offsetY = object3D.rotation.offset ? object3D.rotation.offset.y : 0
|
||||||
|
|
||||||
|
// On mobile, do camera rotation with touch events and sensors.
|
||||||
|
object3D.rotation.x = this.magicWindowDeltaEuler.x + offsetX + pitchObject.rotation.x;
|
||||||
|
object3D.rotation.y = this.magicWindowDeltaEuler.y + offsetY + yawObject.rotation.y;
|
||||||
|
object3D.rotation.z = this.magicWindowDeltaEuler.z;
|
||||||
|
object3D.matrixAutoUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
el.setAttribute('look-controls','')
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,27 @@
|
||||||
|
// poor man's way to move forward using hand gesture pinch
|
||||||
|
|
||||||
|
window.AFRAME.registerComponent('pinch-to-teleport', {
|
||||||
|
schema:{
|
||||||
|
rig: {type: "selector"}
|
||||||
|
},
|
||||||
|
init: function(){
|
||||||
|
this.el.addEventListener("pinchended", () => {
|
||||||
|
// get the cameras world direction
|
||||||
|
let direction = new THREE.Vector3()
|
||||||
|
this.el.sceneEl.camera.getWorldDirection(direction);
|
||||||
|
// multiply the direction by a "speed" factor
|
||||||
|
direction.multiplyScalar(0.4)
|
||||||
|
// get the current position
|
||||||
|
var pos = player.getAttribute("position")
|
||||||
|
// add the direction vector
|
||||||
|
pos.x += direction.x
|
||||||
|
pos.z += direction.z
|
||||||
|
// set the new position
|
||||||
|
this.data.rig.setAttribute("position", pos);
|
||||||
|
// !!! NOTE - it would be more efficient to do the
|
||||||
|
// position change on the players THREE.Object:
|
||||||
|
// `player.object3D.position.add(direction)`
|
||||||
|
// but it would break "getAttribute("position")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,72 @@
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
window.AFRAME.registerComponent('xrf-wear', {
|
||||||
|
schema:{
|
||||||
|
el: {type:"selector"},
|
||||||
|
position: {type:"vec3"},
|
||||||
|
rotation: {type:"vec3"}
|
||||||
|
},
|
||||||
|
init: function(){
|
||||||
|
$('a-scene').addEventListener('enter-vr', (e) => this.wear(e) )
|
||||||
|
$('a-scene').addEventListener('exit-vr', (e) => this.unwear(e) )
|
||||||
|
},
|
||||||
|
wear: function(){
|
||||||
|
if( !this.wearable ){
|
||||||
|
let d = this.data
|
||||||
|
this.wearable = new THREE.Group()
|
||||||
|
this.el.object3D.children.map( (c) => this.wearable.add(c) )
|
||||||
|
this.wearable.position.set( d.position.x, d.position.y, d.position.z)
|
||||||
|
this.wearable.rotation.set( d.rotation.x, d.rotation.y, d.rotation.z)
|
||||||
|
}
|
||||||
|
this.data.el.object3D.add(this.wearable)
|
||||||
|
},
|
||||||
|
unwear: function(){
|
||||||
|
this.data.el.remove(this.wearable)
|
||||||
|
this.wearable.children.map( (c) => this.el.object3D.add(c) )
|
||||||
|
delete this.wearable
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue