Compare commits
No commits in common. "a2a26dc739bad18f463b00237cba32ccefb00c24" and "ad2fa285a0dbf27c7d9992d03c29ebeab41bb2d3" have entirely different histories.
a2a26dc739
...
ad2fa285a0
|
@ -13,7 +13,7 @@ AFRAME.registerComponent('helloworld-html', {
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
scale: 1,
|
scale: 3,
|
||||||
events: ['click'],
|
events: ['click'],
|
||||||
html: (me) => `<div>
|
html: (me) => `<div>
|
||||||
<div class="pad"> helloworld-html: ${me.data.foo} <b>${me.data.myvalue}</b></span>
|
<div class="pad"> helloworld-html: ${me.data.foo} <b>${me.data.myvalue}</b></span>
|
||||||
|
|
|
@ -12,7 +12,7 @@ AFRAME.registerComponent('helloworld-htmlform', {
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
scale: 1,
|
scale: 3,
|
||||||
events: ['click','input'],
|
events: ['click','input'],
|
||||||
html: (me) => `<div class="light">
|
html: (me) => `<div class="light">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|
|
@ -12,7 +12,7 @@ AFRAME.registerComponent('helloworld-window', {
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
scale: 1,
|
scale: 3,
|
||||||
events: ['click','keydown'],
|
events: ['click','keydown'],
|
||||||
html: (me) => `<div>
|
html: (me) => `<div>
|
||||||
<div class="pad"> ${me.data.foo} <b>${me.data.myvalue}</b></span>
|
<div class="pad"> ${me.data.foo} <b>${me.data.myvalue}</b></span>
|
||||||
|
@ -25,6 +25,7 @@ AFRAME.registerComponent('helloworld-window', {
|
||||||
|
|
||||||
// component events
|
// component events
|
||||||
html: function( ){ console.log("html-mesh requirement mounted") },
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
stylis: function( ){ console.log("stylis requirement mounted") },
|
||||||
|
|
||||||
// combined AFRAME+DOM reactive events
|
// combined AFRAME+DOM reactive events
|
||||||
click: function(e){ }, //
|
click: function(e){ }, //
|
||||||
|
|
|
@ -5,15 +5,10 @@ AFRAME.registerComponent('helloworld', {
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
this.el.object3D.visible = false
|
this.el.object3D.visible = false
|
||||||
|
this.el.setAttribute("geometry","primitive: octahedron")
|
||||||
this.el.innerHTML = `
|
this.interval = setInterval( () => {
|
||||||
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
|
this.data.myvalue = ((this.data.myvalue||1.0) + 0.25) % 1
|
||||||
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
|
}, 400 )
|
||||||
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
|
|
||||||
<a-entity position="0 1.8 -3" scale="10 10 10" text="value: ${this.data.foo}; align:center; color:#888"></a-entity>
|
|
||||||
`
|
|
||||||
|
|
||||||
this.interval = setInterval( () => this.data.wireframe = !this.data.wireframe, 500 )
|
|
||||||
},
|
},
|
||||||
|
|
||||||
requires:{
|
requires:{
|
||||||
|
@ -31,9 +26,7 @@ AFRAME.registerComponent('helloworld', {
|
||||||
},
|
},
|
||||||
|
|
||||||
// reactive this.data value demo
|
// reactive this.data value demo
|
||||||
wireframe:function( ){
|
myvalue:function( ){ this.el.object3D.children[0].scale.y = this.data.myvalue }
|
||||||
this.el.object3D.traverse( (obj) => obj.material && (obj.material.wireframe = this.data.wireframe) )
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
AFRAME.registerComponent('helloworld', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
requires:{
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
|
||||||
|
},
|
||||||
|
|
||||||
|
dom: {
|
||||||
|
scale: 3,
|
||||||
|
events: ['click','input'],
|
||||||
|
html: (me) => `
|
||||||
|
<div id="${me.el.uid}" class="modal hello">
|
||||||
|
<div class="top">
|
||||||
|
<div class="title">Hello world</div>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Colour</legend>
|
||||||
|
<input type="radio" id="color-red" name="color" value="red" checked><label for="color-red"> Red</label><br>
|
||||||
|
<input type="radio" id="color-blue" name="color" value="blue"><label for="color-blue"> Blue</label><br>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Material:</legend>
|
||||||
|
<input id="material-wireframe" type="checkbox" name="wireframe"><label for="material-wireframe"> Wireframe</label><br>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Size: <span id="value"></span></legend>
|
||||||
|
<input type="range" min="0.1" max="2" value="1" step="0.01" id="myRange" style="background-color: transparent;">
|
||||||
|
</fieldset>
|
||||||
|
<button>hello</button>
|
||||||
|
</div>`,
|
||||||
|
css: `
|
||||||
|
/*
|
||||||
|
* * HTML-2-WebGL limitations / guidelines for html-mesh compatibility:
|
||||||
|
* no icon libraries (favicon e.g.)
|
||||||
|
* in case of 'border-radius: 2px 3px 4px 5px' (2px will apply to all corners)
|
||||||
|
* dont use transform: scale(1.2) e.g.
|
||||||
|
* */
|
||||||
|
.modal.hello {
|
||||||
|
position:relative;
|
||||||
|
top:0;
|
||||||
|
width:200px;
|
||||||
|
.title { font-weight:bold; } /* modern nested buildless css thanks to stylis */
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
|
||||||
|
events:{
|
||||||
|
|
||||||
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
stylis: function( ){ console.log("stylis requirement mounted") },
|
||||||
|
|
||||||
|
DOMready: function(e){
|
||||||
|
// our reactive dom element has been added to the dom (DOMElement = this.el.dom)
|
||||||
|
},
|
||||||
|
|
||||||
|
click: function(e){ // a click was detected on this.el.dom or AFRAME entity
|
||||||
|
let el = e.detail.target || e.detail.srcElement
|
||||||
|
if( !el ) return
|
||||||
|
if( el.className.match("fold") ) this.el.toggleFold()
|
||||||
|
if( el.className.match("close") ) this.el.close()
|
||||||
|
},
|
||||||
|
|
||||||
|
input: function(e){
|
||||||
|
if( !e.detail.target ) return
|
||||||
|
if( e.detail.target.id == 'myRange' ) this.data.value = e.detail.target.value // reactive demonstration
|
||||||
|
},
|
||||||
|
|
||||||
|
value: function(e){ this.el.dom.querySelector("#value").innerHTML = e.detail.v }, // auto-emitted when this.data.value gets updated
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
this.require( this.requires )
|
||||||
|
|
||||||
|
this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
|
||||||
|
this.scene.addEventListener('apps:XR', () => {
|
||||||
|
this.el.setAttribute('visible', true)
|
||||||
|
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Hello world",
|
||||||
|
"name": "Hello world",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/images/icons-vector.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "/?source=pwa",
|
||||||
|
"start_url": "/?source=pwa",
|
||||||
|
"background_color": "#3367D6",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#3367D6",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "What is the latest news?",
|
||||||
|
"cli":{
|
||||||
|
"usage": "helloworld <type> [options]",
|
||||||
|
"example": "helloworld news",
|
||||||
|
"args":{
|
||||||
|
"--latest": {type:"string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"short_name": "Today",
|
||||||
|
"description": "View weather information for today",
|
||||||
|
"url": "/today?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Hello world information",
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"src": "/images/screenshot1.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "540x720",
|
||||||
|
"form_factor": "narrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"help":`
|
||||||
|
Helloworld application
|
||||||
|
|
||||||
|
This is a help file which describes the application.
|
||||||
|
It will be rendered thru troika text, and will contain
|
||||||
|
headers based on non-punctualized lines separated by linebreaks,
|
||||||
|
in above's case "\nHelloworld application\n" will qualify as header.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -15,13 +15,6 @@ AFRAME.registerComponent('xrfragments', {
|
||||||
// requires are loaded
|
// requires are loaded
|
||||||
ready: function(e){
|
ready: function(e){
|
||||||
this.el.setAttribute("xrf","https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
|
this.el.setAttribute("xrf","https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
|
||||||
|
|
||||||
let ARbutton = document.querySelector('.a-enter-ar-button')
|
|
||||||
if( ARbutton ){
|
|
||||||
ARbutton.addEventListener('click', () => {
|
|
||||||
AFRAME.XRF.reset()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
launcher: function(){
|
launcher: function(){
|
||||||
|
|
66
com/app.js
66
com/app.js
|
@ -5,8 +5,6 @@ AFRAME.app = new Proxy({
|
||||||
|
|
||||||
order:0,
|
order:0,
|
||||||
|
|
||||||
components: {}, // component-strings in this array are automatically
|
|
||||||
// added to each app
|
|
||||||
add(component, entity){
|
add(component, entity){
|
||||||
// categorize by component to prevent similar apps loading duplicate dependencies simultaniously
|
// categorize by component to prevent similar apps loading duplicate dependencies simultaniously
|
||||||
this[component] = this[component] || []
|
this[component] = this[component] || []
|
||||||
|
@ -36,7 +34,7 @@ AFRAME.app = new Proxy({
|
||||||
* This is the abstract 'app' component
|
* This is the abstract 'app' component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
appComponent = {
|
AFRAME.registerComponent('app', {
|
||||||
schema:{
|
schema:{
|
||||||
"uri":{ type:"string"}
|
"uri":{ type:"string"}
|
||||||
},
|
},
|
||||||
|
@ -45,11 +43,7 @@ appComponent = {
|
||||||
"app:ready": function(){
|
"app:ready": function(){
|
||||||
let {id,component,type} = this.parseAppURI(this.data.uri)
|
let {id,component,type} = this.parseAppURI(this.data.uri)
|
||||||
AFRAME.app[component].map( (app) => {
|
AFRAME.app[component].map( (app) => {
|
||||||
if( !app.el.getAttribute(component) ){
|
if( !app.el.getAttribute(component) ) app.el.setAttribute(component,app.data)
|
||||||
if( AFRAME.components[ component ] ){
|
|
||||||
app.el.setAttribute(component,app.data)
|
|
||||||
}else console.warn(`${component} was not fully downloaded yet (${app.data.uri})`)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
"requires:ready": function(){
|
"requires:ready": function(){
|
||||||
|
@ -121,10 +115,7 @@ appComponent = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
})
|
||||||
|
|
||||||
AFRAME.registerComponent('app', appComponent)
|
|
||||||
AFRAME.registerComponent('com', appComponent)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Here are monkeypatched AFRAME component prototype functions
|
* Here are monkeypatched AFRAME component prototype functions
|
||||||
|
@ -160,16 +151,22 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
overlay.id = "overlay"
|
overlay.id = "overlay"
|
||||||
document.body.appendChild(overlay)
|
document.body.appendChild(overlay)
|
||||||
document.querySelector("a-scene").setAttribute("webxr","overlayElement:#overlay")
|
document.querySelector("a-scene").setAttribute("webxr","overlayElement:#overlay")
|
||||||
|
// let menu = document.createElement('div')
|
||||||
|
// menu.id = 'iconmenu'
|
||||||
|
// document.body.appendChild(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reactify = (el,aframe) => new Proxy(this.data,{
|
// // add menu button
|
||||||
get(me,k,v) { return me[k]
|
// if( this.manifest && this.manifest.icons ){
|
||||||
},
|
// let btn = document.createElement('button')
|
||||||
set(me,k,v){
|
// btn.app = this
|
||||||
me[k] = v
|
// if( this.manifest.icons.length ){
|
||||||
aframe.emit(k,{el,k,v})
|
// btn.innerHTML = `<img src="${this.manifest.icons[0].src}"/>`
|
||||||
}
|
// }else btn.innerText = this.manifest.short_name
|
||||||
})
|
// btn.setAttribute("alt", this.manifest.name )
|
||||||
|
// btn.addEventListener('click', (e) => this.el.emit('launcher',{}) )
|
||||||
|
// document.querySelector("#iconmenu").appendChild(btn)
|
||||||
|
// }
|
||||||
|
|
||||||
// reactify components with dom-definition
|
// reactify components with dom-definition
|
||||||
if( this.data.uri && this.dom && !this.el.dom ){
|
if( this.data.uri && this.dom && !this.el.dom ){
|
||||||
|
@ -177,6 +174,14 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
tasks = {
|
tasks = {
|
||||||
|
|
||||||
createReactiveDOMElement: () => {
|
createReactiveDOMElement: () => {
|
||||||
|
const reactify = (el,aframe) => new Proxy(this.data,{
|
||||||
|
get(me,k,v) { return me[k]
|
||||||
|
},
|
||||||
|
set(me,k,v){
|
||||||
|
me[k] = v
|
||||||
|
aframe.emit(k,{el,k,v})
|
||||||
|
}
|
||||||
|
})
|
||||||
this.el.dom = document.createElement('div')
|
this.el.dom = document.createElement('div')
|
||||||
this.el.dom.className = this.parseAppURI(this.data.uri).component
|
this.el.dom.className = this.parseAppURI(this.data.uri).component
|
||||||
this.el.dom.innerHTML = this.dom.html(this)
|
this.el.dom.innerHTML = this.dom.html(this)
|
||||||
|
@ -207,13 +212,6 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
return tasks
|
return tasks
|
||||||
},
|
},
|
||||||
|
|
||||||
initAutoComponents: () => {
|
|
||||||
for ( let i in AFRAME.app.components ) {
|
|
||||||
this.el.setAttribute( i, AFRAME.app.components[i] )
|
|
||||||
}
|
|
||||||
return tasks
|
|
||||||
},
|
|
||||||
|
|
||||||
triggerKeyboardForInputs: () => {
|
triggerKeyboardForInputs: () => {
|
||||||
// https://developer.oculus.com/documentation/web/webxr-keyboard ;
|
// https://developer.oculus.com/documentation/web/webxr-keyboard ;
|
||||||
[...this.el.dom.querySelectorAll('[type=text]')].map( (input) => {
|
[...this.el.dom.querySelectorAll('[type=text]')].map( (input) => {
|
||||||
|
@ -234,24 +232,16 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
.scaleDOMvsXR()
|
.scaleDOMvsXR()
|
||||||
.triggerKeyboardForInputs()
|
.triggerKeyboardForInputs()
|
||||||
.setupListeners()
|
.setupListeners()
|
||||||
.initAutoComponents()
|
|
||||||
|
|
||||||
document.querySelector('#overlay').appendChild(this.el.dom)
|
document.querySelector('#overlay').appendChild(this.el.dom)
|
||||||
this.el.emit('DOMready',{el: this.el.dom})
|
this.el.emit('DOMready',{el: this.el.dom})
|
||||||
|
|
||||||
}else this.data = reactify( this.el, this.el )
|
}
|
||||||
|
|
||||||
// assign unique app id
|
// assign unique app id
|
||||||
if( !this.el.uid ) this.el.uid = '_'+String(Math.random()).substr(10)
|
if( !this.el.uid ) this.el.uid = '_'+String(Math.random()).substr(10)
|
||||||
|
|
||||||
// require coms
|
if( this.requires ) this.require( this.requires, 'requires:ready' )
|
||||||
let requires = {}
|
else this.el.emit('requires:ready')
|
||||||
for ( let i in AFRAME.app.components ) {
|
|
||||||
if( !AFRAME.components[i] ) requires[i] = AFRAME.app.components[i]
|
|
||||||
}
|
|
||||||
if( this.requires ) requires = {...requires, ...this.requires }
|
|
||||||
if( Object.values(requires).length ) this.require( requires, 'requires:ready' )
|
|
||||||
else this.el.emit('requires:ready' )
|
|
||||||
|
|
||||||
// mark app as being initialized
|
// mark app as being initialized
|
||||||
this.isApp = true
|
this.isApp = true
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
// 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) )
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,67 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,65 +0,0 @@
|
||||||
// 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','')
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,27 +0,0 @@
|
||||||
// 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")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -1,72 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,26 +0,0 @@
|
||||||
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