allow empty xrf

This commit is contained in:
Leon van Kammen 2024-01-03 15:55:23 +00:00
parent 13f96e0506
commit 49cdf18472
7 changed files with 4797 additions and 345 deletions

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Sat Dec 30 09:46:42 PM UTC 2023 * v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -2341,6 +2341,8 @@ xrf.addEventListener('t', (opts) => {
}) })
window.AFRAME.registerComponent('xrf', { window.AFRAME.registerComponent('xrf', {
schema: { schema: {
http: { type:'string'},
https: { type:'string'},
}, },
init: function () { init: function () {
if( !AFRAME.XRF ){ if( !AFRAME.XRF ){
@ -2350,112 +2352,116 @@ window.AFRAME.registerComponent('xrf', {
camera.setAttribute('xrf-fade','') camera.setAttribute('xrf-fade','')
AFRAME.fade = camera.components['xrf-fade'] AFRAME.fade = camera.components['xrf-fade']
let aScene = document.querySelector('a-scene') let aScene = AFRAME.scenes[0]
aScene.addEventListener('loaded', () => {
// enable XR fragments // enable XR fragments
let XRF = AFRAME.XRF = xrf.init({ let XRF = AFRAME.XRF = xrf.init({
THREE, THREE,
camera: aScene.camera, camera: aScene.camera,
scene: aScene.object3D, scene: aScene.object3D,
renderer: aScene.renderer, renderer: aScene.renderer,
loaders: { loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments? gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
glb: THREE.GLTFLoader, glb: THREE.GLTFLoader,
obj: THREE.OBJLoader obj: THREE.OBJLoader
}
})
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
ARbutton = document.querySelector('.a-enter-ar-button')
VRbutton = document.querySelector('.a-enter-vr-button')
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
xrf.addEventListener('navigateLoaded', () => {
setTimeout( () => AFRAME.fade.out(),500)
// *TODO* this does not really belong here perhaps
let blinkControls = document.querySelector('[blink-controls]')
if( blinkControls ){
let els = xrf.getCollisionMeshes()
let invisible = false
els.map( (mesh) => {
if( !invisible ){
invisible = mesh.material.clone()
invisible.visible = false
}
mesh.material = invisible
let el = document.createElement("a-entity")
el.setAttribute("xrf-get", mesh.name )
el.setAttribute("class","floor")
$('a-scene').appendChild(el)
})
blinkControls.components['blink-controls'].update({collisionEntities:true})
}
})
xrf.addEventListener('href', (opts) => {
if( opts.click){
let p = opts.promise()
let url = opts.xrf.string
let isLocal = url.match(/^#/)
let hasPos = url.match(/pos=/)
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
if( isLocal && hasPos ){
// local teleports only
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else if( !isLocal ){
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}else p.resolve()
}
})
// convert href's to a-entity's so AFRAME
// raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts;
let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
} }
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene
xrf.addEventListener('reset', (opts) => {
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
})
AFRAME.XRF.navigator.to(this.data)
.then( (model) => {
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
gets.map( (g) => g.emit('update') )
})
aScene.emit('XRF',{})
// enable gaze-click on Mobile VR
aScene.setAttribute('xrf-gaze','')
}) })
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
ARbutton = document.querySelector('.a-enter-ar-button')
VRbutton = document.querySelector('.a-enter-vr-button')
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
xrf.addEventListener('navigateLoaded', () => {
setTimeout( () => AFRAME.fade.out(),500)
// *TODO* this does not really belong here perhaps
let blinkControls = document.querySelector('[blink-controls]')
if( blinkControls ){
let els = xrf.getCollisionMeshes()
let invisible = false
els.map( (mesh) => {
if( !invisible ){
invisible = mesh.material.clone()
invisible.visible = false
}
mesh.material = invisible
let el = document.createElement("a-entity")
el.setAttribute("xrf-get", mesh.name )
el.setAttribute("class","floor")
$('a-scene').appendChild(el)
})
blinkControls.components['blink-controls'].update({collisionEntities:true})
}
})
xrf.addEventListener('href', (opts) => {
if( opts.click){
let p = opts.promise()
let url = opts.xrf.string
let isLocal = url.match(/^#/)
let hasPos = url.match(/pos=/)
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
if( isLocal && hasPos ){
// local teleports only
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else if( !isLocal ){
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}else p.resolve()
}
})
// convert href's to a-entity's so AFRAME
// raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts;
let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
}
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene
xrf.addEventListener('reset', (opts) => {
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
})
if( typeof this.data === 'string' || this.data.http || this.data.https ){
let url
if( typeof this.data === 'string' ) url = this.data
if( this.data.http ) url = `http:${this.data.http}`
if( this.data.https ) url = `https:${this.data.https}`
AFRAME.XRF.navigator.to( url )
.then( (model) => {
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
gets.map( (g) => g.emit('update') )
})
}
aScene.emit('XRF',{})
// enable gaze-click on Mobile VR
aScene.setAttribute('xrf-gaze','')
} }
if( typeof this.data == "string" ){ if( typeof this.data == "string" ){

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Sat Dec 30 09:46:41 PM UTC 2023 * v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -2339,6 +2339,8 @@ xrf.addEventListener('t', (opts) => {
}) })
window.AFRAME.registerComponent('xrf', { window.AFRAME.registerComponent('xrf', {
schema: { schema: {
http: { type:'string'},
https: { type:'string'},
}, },
init: function () { init: function () {
if( !AFRAME.XRF ){ if( !AFRAME.XRF ){
@ -2348,112 +2350,116 @@ window.AFRAME.registerComponent('xrf', {
camera.setAttribute('xrf-fade','') camera.setAttribute('xrf-fade','')
AFRAME.fade = camera.components['xrf-fade'] AFRAME.fade = camera.components['xrf-fade']
let aScene = document.querySelector('a-scene') let aScene = AFRAME.scenes[0]
aScene.addEventListener('loaded', () => {
// enable XR fragments // enable XR fragments
let XRF = AFRAME.XRF = xrf.init({ let XRF = AFRAME.XRF = xrf.init({
THREE, THREE,
camera: aScene.camera, camera: aScene.camera,
scene: aScene.object3D, scene: aScene.object3D,
renderer: aScene.renderer, renderer: aScene.renderer,
loaders: { loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments? gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
glb: THREE.GLTFLoader, glb: THREE.GLTFLoader,
obj: THREE.OBJLoader obj: THREE.OBJLoader
}
})
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
ARbutton = document.querySelector('.a-enter-ar-button')
VRbutton = document.querySelector('.a-enter-vr-button')
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
xrf.addEventListener('navigateLoaded', () => {
setTimeout( () => AFRAME.fade.out(),500)
// *TODO* this does not really belong here perhaps
let blinkControls = document.querySelector('[blink-controls]')
if( blinkControls ){
let els = xrf.getCollisionMeshes()
let invisible = false
els.map( (mesh) => {
if( !invisible ){
invisible = mesh.material.clone()
invisible.visible = false
}
mesh.material = invisible
let el = document.createElement("a-entity")
el.setAttribute("xrf-get", mesh.name )
el.setAttribute("class","floor")
$('a-scene').appendChild(el)
})
blinkControls.components['blink-controls'].update({collisionEntities:true})
}
})
xrf.addEventListener('href', (opts) => {
if( opts.click){
let p = opts.promise()
let url = opts.xrf.string
let isLocal = url.match(/^#/)
let hasPos = url.match(/pos=/)
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
if( isLocal && hasPos ){
// local teleports only
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else if( !isLocal ){
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}else p.resolve()
}
})
// convert href's to a-entity's so AFRAME
// raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts;
let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
} }
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene
xrf.addEventListener('reset', (opts) => {
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
})
AFRAME.XRF.navigator.to(this.data)
.then( (model) => {
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
gets.map( (g) => g.emit('update') )
})
aScene.emit('XRF',{})
// enable gaze-click on Mobile VR
aScene.setAttribute('xrf-gaze','')
}) })
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
ARbutton = document.querySelector('.a-enter-ar-button')
VRbutton = document.querySelector('.a-enter-vr-button')
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
xrf.addEventListener('navigateLoaded', () => {
setTimeout( () => AFRAME.fade.out(),500)
// *TODO* this does not really belong here perhaps
let blinkControls = document.querySelector('[blink-controls]')
if( blinkControls ){
let els = xrf.getCollisionMeshes()
let invisible = false
els.map( (mesh) => {
if( !invisible ){
invisible = mesh.material.clone()
invisible.visible = false
}
mesh.material = invisible
let el = document.createElement("a-entity")
el.setAttribute("xrf-get", mesh.name )
el.setAttribute("class","floor")
$('a-scene').appendChild(el)
})
blinkControls.components['blink-controls'].update({collisionEntities:true})
}
})
xrf.addEventListener('href', (opts) => {
if( opts.click){
let p = opts.promise()
let url = opts.xrf.string
let isLocal = url.match(/^#/)
let hasPos = url.match(/pos=/)
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
if( isLocal && hasPos ){
// local teleports only
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else if( !isLocal ){
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}else p.resolve()
}
})
// convert href's to a-entity's so AFRAME
// raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts;
let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
}
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene
xrf.addEventListener('reset', (opts) => {
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
})
if( typeof this.data === 'string' || this.data.http || this.data.https ){
let url
if( typeof this.data === 'string' ) url = this.data
if( this.data.http ) url = `http:${this.data.http}`
if( this.data.https ) url = `https:${this.data.https}`
AFRAME.XRF.navigator.to( url )
.then( (model) => {
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
gets.map( (g) => g.emit('update') )
})
}
aScene.emit('XRF',{})
// enable gaze-click on Mobile VR
aScene.setAttribute('xrf-gaze','')
} }
if( typeof this.data == "string" ){ if( typeof this.data == "string" ){

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Sat Dec 30 09:46:42 PM UTC 2023 * v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Sat Dec 30 09:46:42 PM UTC 2023 * v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */

View file

@ -30,42 +30,44 @@
<!-- --> <!-- -->
<!-- everything below is completely optional, but handy to add a quick menu / connectivity to your space --> <!-- everything below is completely optional, but handy to add a quick menu / connectivity to your space -->
<!-- --> <!-- -->
<script>
document.addEventListener('$menu:ready', (e) => {
let {$menu} = e.detail
// add your menubuttons:
$menu.buttons = $menu.buttons.concat([
`<a class="btn" aria-label="button" aria-title="menu button" onclick="$menu.showAbout()"><i class="gg-info"></i>&nbsp;&nbsp;&nbsp;about</a><br>`
])
$menu.collapsed = false
$menu.showAbout = () => window.notify(`
<h1>💁 Hi there!</h1>
This XR fragments experience works almost anywhere.<br>
Allowing rich audiovisual events with(out)<br>
VR/AR headsets (it's awesome though ♥️)<br>
<br>
<b>This uses only open standards:</b><br>
📦 surf 3D models using URLs<br>
🧑‍🤝‍🧑 <b>instant</b> meetings inside hyperlinked 3D models<br>
🚫 <b>zero proprietary</b> tech/game engines or platforms<br>
🙈 unhosted, privacy-friendly, avatar-agnostic scenes<br>
🔗 <a href="https://xrfragment.org" target="_blank">XR Fragments</a> for 3D <b>hyper-linking</b> & navigation<br>
📷 Serverless & encrypted <a href="https://webrtc.org" target="_blank">P2P WebRTC</a> using <a href="https://github.com/dmotz/trystero" target="_blank">trystero</a><br>
🦍 <a href="https://immersiveweb.dev" target="_blank">WebXR</a> using <a href="https://aframe.io" target="_blank">AFRAME</a> + <a href="https://three.org" target="_blank">Three.js</a><br>
🙉 go selfhost <a href="https://github.com/coderofsalvation/xrfragment-helloworld">worlds-in-a-webpage</a><br>
♥️ Be sustainable: go 100% <a href="https://www.forbes.com/sites/adrianbridgwater/2023/02/06/the-future-for-open-source/" target="_blank">opensource</a>
<br><br>
<a href="https://nlnet.nl" target="_blank">
<img src="https://nlnet.nl/image/logo_nlnet.svg" width="100"/>
</a>
<br><br>
`,{timeout:false})
})
</script>
<script src="./../../../dist/aframe-blink-controls.min.js"></script> <!-- teleporting using controllers --> <script src="./../../../dist/aframe-blink-controls.min.js"></script> <!-- teleporting using controllers -->
<script src="./../../../dist/xrfragment.plugin.frontend.js"></script> <!-- menu + chat interface --> <script src="./../../../dist/xrfragment.plugin.frontend.js"></script> <!-- menu + chat interface -->
<script src="./../../../dist/xrfragment.plugin.p2p.js"></script> <!-- serverless p2p connectivity --> <script src="./../../../dist/xrfragment.plugin.p2p.js"></script> <!-- serverless p2p connectivity -->
<script src="./../../../dist/xrfragment.plugin.matrix.js"></script> <!-- matrix connectivity --> <script src="./../../../dist/xrfragment.plugin.matrix.js"></script> <!-- matrix connectivity -->
<script>
$menu.logo = 'logo_file_or_url_here'
$menu.morelabel = '⚡'
// add your menubuttons:
$menu.buttons = $menu.buttons.concat([`<a class="btn" aria-label="button" aria-title="menu button" aria-description="about menu" onclick="$menu.showAbout()">💁 about</a><br>`])
$menu.collapsed = false
$menu.showAbout = () => window.notify(`
<h1>💁 Hi there!</h1>
This XR fragments experience works almost anywhere.<br>
Allowing rich audiovisual events with(out)<br>
VR/AR headsets (it's awesome though ♥️)<br>
<br>
<b>This uses only open standards:</b><br>
📦 surf 3D models using URLs<br>
🧑‍🤝‍🧑 <b>instant</b> meetings inside hyperlinked 3D models<br>
🚫 <b>zero proprietary</b> tech/game engines or platforms<br>
🙈 unhosted, privacy-friendly, avatar-agnostic scenes<br>
🔗 <a href="https://xrfragment.org" target="_blank">XR Fragments</a> for 3D <b>hyper-linking</b> & navigation<br>
📷 Serverless & encrypted <a href="https://webrtc.org" target="_blank">P2P WebRTC</a> using <a href="https://github.com/dmotz/trystero" target="_blank">trystero</a><br>
🦍 <a href="https://immersiveweb.dev" target="_blank">WebXR</a> using <a href="https://aframe.io" target="_blank">AFRAME</a> + <a href="https://three.org" target="_blank">Three.js</a><br>
🙉 go selfhost <a href="https://github.com/coderofsalvation/xrfragment-helloworld">worlds-in-a-webpage</a><br>
♥️ Be sustainable: go 100% <a href="https://www.forbes.com/sites/adrianbridgwater/2023/02/06/the-future-for-open-source/" target="_blank">opensource</a>
<br><br>
<a href="https://nlnet.nl" target="_blank">
<img src="https://nlnet.nl/image/logo_nlnet.svg" width="100"/>
</a>
<br><br>
`,{timeout:false})
$menu.install(xrf)
</script>
</body> </body>
</html> </html>

View file

@ -1,5 +1,7 @@
window.AFRAME.registerComponent('xrf', { window.AFRAME.registerComponent('xrf', {
schema: { schema: {
http: { type:'string'},
https: { type:'string'},
}, },
init: function () { init: function () {
if( !AFRAME.XRF ){ if( !AFRAME.XRF ){
@ -9,112 +11,116 @@ window.AFRAME.registerComponent('xrf', {
camera.setAttribute('xrf-fade','') camera.setAttribute('xrf-fade','')
AFRAME.fade = camera.components['xrf-fade'] AFRAME.fade = camera.components['xrf-fade']
let aScene = document.querySelector('a-scene') let aScene = AFRAME.scenes[0]
aScene.addEventListener('loaded', () => {
// enable XR fragments // enable XR fragments
let XRF = AFRAME.XRF = xrf.init({ let XRF = AFRAME.XRF = xrf.init({
THREE, THREE,
camera: aScene.camera, camera: aScene.camera,
scene: aScene.object3D, scene: aScene.object3D,
renderer: aScene.renderer, renderer: aScene.renderer,
loaders: { loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments? gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
glb: THREE.GLTFLoader, glb: THREE.GLTFLoader,
obj: THREE.OBJLoader obj: THREE.OBJLoader
}
})
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
ARbutton = document.querySelector('.a-enter-ar-button')
VRbutton = document.querySelector('.a-enter-vr-button')
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
xrf.addEventListener('navigateLoaded', () => {
setTimeout( () => AFRAME.fade.out(),500)
// *TODO* this does not really belong here perhaps
let blinkControls = document.querySelector('[blink-controls]')
if( blinkControls ){
let els = xrf.getCollisionMeshes()
let invisible = false
els.map( (mesh) => {
if( !invisible ){
invisible = mesh.material.clone()
invisible.visible = false
}
mesh.material = invisible
let el = document.createElement("a-entity")
el.setAttribute("xrf-get", mesh.name )
el.setAttribute("class","floor")
$('a-scene').appendChild(el)
})
blinkControls.components['blink-controls'].update({collisionEntities:true})
}
})
xrf.addEventListener('href', (opts) => {
if( opts.click){
let p = opts.promise()
let url = opts.xrf.string
let isLocal = url.match(/^#/)
let hasPos = url.match(/pos=/)
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
if( isLocal && hasPos ){
// local teleports only
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else if( !isLocal ){
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}else p.resolve()
}
})
// convert href's to a-entity's so AFRAME
// raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts;
let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
} }
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene
xrf.addEventListener('reset', (opts) => {
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
})
AFRAME.XRF.navigator.to(this.data)
.then( (model) => {
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
gets.map( (g) => g.emit('update') )
})
aScene.emit('XRF',{})
// enable gaze-click on Mobile VR
aScene.setAttribute('xrf-gaze','')
}) })
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
ARbutton = document.querySelector('.a-enter-ar-button')
VRbutton = document.querySelector('.a-enter-vr-button')
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
xrf.addEventListener('navigateLoaded', () => {
setTimeout( () => AFRAME.fade.out(),500)
// *TODO* this does not really belong here perhaps
let blinkControls = document.querySelector('[blink-controls]')
if( blinkControls ){
let els = xrf.getCollisionMeshes()
let invisible = false
els.map( (mesh) => {
if( !invisible ){
invisible = mesh.material.clone()
invisible.visible = false
}
mesh.material = invisible
let el = document.createElement("a-entity")
el.setAttribute("xrf-get", mesh.name )
el.setAttribute("class","floor")
$('a-scene').appendChild(el)
})
blinkControls.components['blink-controls'].update({collisionEntities:true})
}
})
xrf.addEventListener('href', (opts) => {
if( opts.click){
let p = opts.promise()
let url = opts.xrf.string
let isLocal = url.match(/^#/)
let hasPos = url.match(/pos=/)
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
if( isLocal && hasPos ){
// local teleports only
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else if( !isLocal ){
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}else p.resolve()
}
})
// convert href's to a-entity's so AFRAME
// raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts;
let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
}
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene
xrf.addEventListener('reset', (opts) => {
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
})
if( typeof this.data === 'string' || this.data.http || this.data.https ){
let url
if( typeof this.data === 'string' ) url = this.data
if( this.data.http ) url = `http:${this.data.http}`
if( this.data.https ) url = `https:${this.data.https}`
AFRAME.XRF.navigator.to( url )
.then( (model) => {
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
gets.map( (g) => g.emit('update') )
})
}
aScene.emit('XRF',{})
// enable gaze-click on Mobile VR
aScene.setAttribute('xrf-gaze','')
} }
if( typeof this.data == "string" ){ if( typeof this.data == "string" ){